From e544d6a6dbb8cfd880c72211057b43720e75f7a4 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Thu, 9 Mar 2017 11:24:52 -0800 Subject: [PATCH] refactor api and fix bugs (#1088) * fix weird double-embed issue * fix users/current api if not logged in * turbocharge the api * fix docs --- app/controllers/api/v2/users_controller.rb | 1 + .../api/v2/application_serializer.rb | 61 +++++++++---------- app/serializers/api/v2/map_serializer.rb | 2 +- app/serializers/api/v2/mapping_serializer.rb | 2 +- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/app/controllers/api/v2/users_controller.rb b/app/controllers/api/v2/users_controller.rb index 3f60c410..092a95b5 100644 --- a/app/controllers/api/v2/users_controller.rb +++ b/app/controllers/api/v2/users_controller.rb @@ -3,6 +3,7 @@ module Api module V2 class UsersController < RestfulController def current + raise Pundit::NotAuthorizedError if current_user.nil? @user = current_user authorize @user show # delegate to the normal show function diff --git a/app/serializers/api/v2/application_serializer.rb b/app/serializers/api/v2/application_serializer.rb index 81772577..c0399db6 100644 --- a/app/serializers/api/v2/application_serializer.rb +++ b/app/serializers/api/v2/application_serializer.rb @@ -13,50 +13,49 @@ module Api @embeds ||= (scope[:embeds] || []).select { |e| self.class.embeddable.keys.include?(e) } end - # self.embeddable might look like this: - # creator: { attr: :first_creator, serializer: UserSerializer } - # contributors: { serializer: UserSerializer} - # This method will remove the :attr key if the underlying attribute name - # is different than the name provided in the final json output. All other keys - # in the hash will be passed to the ActiveModel::Serializer `attribute` method - # directly (e.g. serializer in the examples will be passed). - # - # This setup means if you passed this self.embeddable config and sent no - # ?embed= query param with your API request, you would get the regular attributes - # plus creator_id and contributor_ids. If you passed ?embed=creator,contributors - # then instead of an id and an array of ids, you would get a serialized user - # (the first_creator) and an array of serialized users (the contributors). + # Here's an example object that could be passed in self.embeddable: { + # creator: { + # serializer: UserSerializer, + # }, + # collaborators: { + # serializer: UserSerializer + # }, + # topic: {}, + # synapses: {} + # } + # The key has to be in embeddable or it won't show in the response, and the serializer is + # only needed if the key doesn't match a serializer def self.embed_dat embeddable.each_pair do |key, opts| - attr = opts.delete(:attr) || key - if attr.to_s.pluralize == attr.to_s - attribute("#{attr.to_s.singularize}_ids".to_sym, - opts.merge(unless: -> { embeds.include?(key) })) do - Pundit.policy_scope(scope[:current_user], object.send(attr))&.map(&:id) || [] + is_plural = key.to_s.pluralize == key.to_s + id_key = key.to_s.singularize + (is_plural ? '_ids' : '_id') + serializer = opts.delete(:serializer) || "Api::V2::#{key.to_s.singularize.camelize}Serializer".constantize + if is_plural + attribute(id_key.to_sym, opts.merge(unless: -> { embeds.include?(key) })) do + Pundit.policy_scope(scope[:current_user], object.send(key))&.map(&:id) || [] end - has_many(attr, opts.merge(if: -> { embeds.include?(key) })) do - list = Pundit.policy_scope(scope[:current_user], object.send(attr)) || [] - child_serializer = "Api::V2::#{attr.to_s.singularize.camelize}Serializer".constantize + has_many(key, opts.merge(if: -> { embeds.include?(key) })) do + list = Pundit.policy_scope(scope[:current_user], object.send(key)) || [] resource = ActiveModelSerializers::SerializableResource.new( list, - each_serializer: child_serializer, + each_serializer: serializer, scope: scope.merge(embeds: []) ) - resource.as_json + # resource.as_json will return e.g. { users: [ ... ] } for collaborators + # since we can't get the :users key, convert to an array and use .first.second to get the needed values + resource&.as_json&.to_a&.first&.second end else - id_opts = opts.merge(key: "#{key}_id") - attribute("#{attr}_id".to_sym, - id_opts.merge(unless: -> { embeds.include?(key) })) - attribute(key, opts.merge(if: -> { embeds.include?(key) })) do |serializer| - object = serializer.object.send(key) - child_serializer = "Api::V2::#{object.class.name}Serializer".constantize + attribute(id_key.to_sym, opts.merge(unless: -> { embeds.include?(key) })) + attribute(key, opts.merge(if: -> { embeds.include?(key) })) do |parent_serializer| + object = parent_serializer.object.send(key) + next nil if object.nil? resource = ActiveModelSerializers::SerializableResource.new( object, - serializer: child_serializer, + serializer: serializer, scope: scope.merge(embeds: []) ) - resource.as_json + resource&.as_json&.to_a&.first&.second end end end diff --git a/app/serializers/api/v2/map_serializer.rb b/app/serializers/api/v2/map_serializer.rb index 7e090d33..3ba158f9 100644 --- a/app/serializers/api/v2/map_serializer.rb +++ b/app/serializers/api/v2/map_serializer.rb @@ -18,7 +18,7 @@ module Api def self.embeddable { user: {}, - source: {}, + source: { serializer: MapSerializer }, topics: {}, synapses: {}, mappings: {}, diff --git a/app/serializers/api/v2/mapping_serializer.rb b/app/serializers/api/v2/mapping_serializer.rb index 30c9bd7f..d418b31c 100644 --- a/app/serializers/api/v2/mapping_serializer.rb +++ b/app/serializers/api/v2/mapping_serializer.rb @@ -14,7 +14,7 @@ module Api def self.embeddable { user: {}, - updated_by: {}, + updated_by: { serializer: UserSerializer }, map: {} } end