api/app/controllers/spree/api/v2/platform/resource_controller.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Spree
  module Api
    module V2
      module Platform
        class ResourceController < ::Spree::Api::V2::ResourceController
          # doorkeeper scopes usage: https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
          before_action :validate_token_client
          before_action -> { doorkeeper_authorize! :read, :admin }
          before_action -> { doorkeeper_authorize! :write, :admin }, if: :write_request?

          # optional authorization if using a user token instead of app token
          before_action :authorize_spree_user

          # index and show actions are defined in Spree::Api::V2::ResourceController

          def create
            resource = model_class.new(permitted_resource_params)
            ensure_current_store(resource)

            if resource.save
              render_serialized_payload(201) { serialize_resource(resource) }
            else
              render_error_payload(resource.errors)
            end
          end

          def update
            resource.assign_attributes(permitted_resource_params)
            ensure_current_store(resource)

            if resource.save
              render_serialized_payload { serialize_resource(resource) }
            else
              render_error_payload(resource.errors)
            end
          end

          def destroy
            if resource.destroy
              head 204
            else
              render_error_payload(resource.errors)
            end
          end

          protected

          def resource_serializer
            serializer_base_name = model_class.to_s.sub('Spree::', '')
            "Spree::Api::V2::Platform::#{serializer_base_name}Serializer".constantize
          end

          def collection_serializer
            resource_serializer
          end

          # overwriting to utilize ransack gem for filtering
          # https://github.com/activerecord-hackery/ransack#search-matchers
          def collection
            @collection ||= scope.ransack(params[:filter]).result
          end

          # overwriting to skip cancancan check if API is consumed by an application
          def scope
            return super if spree_current_user.present?

            super(skip_cancancan: true)
          end

          # We're overwriting this method because the original one calls `dookreeper_authorize`
          # which breaks our application authorizations defined on top of this controller
          def spree_current_user
            return nil unless doorkeeper_token
            return nil if doorkeeper_token.resource_owner_id.nil?
            return @spree_current_user if @spree_current_user

            @spree_current_user ||= doorkeeper_token.resource_owner
          end

          def access_denied(exception)
            access_denied_401(exception)
          end

          def validate_token_client
            return if doorkeeper_token.nil?

            raise Doorkeeper::Errors::DoorkeeperError if doorkeeper_token.application.nil?
          end

          # if using a user oAuth token we need to check CanCanCan abilities
          # defined in https://github.com/spree/spree/blob/master/core/app/models/spree/ability.rb
          def authorize_spree_user
            return if spree_current_user.nil?

            case action_name
            when 'create'
              spree_authorize! :create, model_class
            when 'destroy'
              spree_authorize! :destroy, resource
            when 'index'
              spree_authorize! :read, model_class
            when 'show'
              spree_authorize! :read, resource
            else
              spree_authorize! :update, resource
            end
          end

          def model_param_name
            model_class.to_s.demodulize.underscore
          end

          def spree_permitted_attributes
            store_ids = if model_class.method_defined?(:stores)
                          [{ store_ids: [] }]
                        else
                          []
                        end

            model_class.json_api_permitted_attributes + store_ids + metadata_params
          end

          def metadata_params
            if model_class.include?(Metadata)
              [{ public_metadata: {}, private_metadata: {} }]
            else
              []
            end
          end

          def permitted_resource_params
            params.require(model_param_name).permit(spree_permitted_attributes)
          end

          def allowed_sort_attributes
            (super << spree_permitted_attributes).uniq.compact
          end

          def write_request?
            %w[put patch post delete].include?(request.request_method.downcase)
          end
        end
      end
    end
  end
end