module Flapjack::Gateways::JSONAPI::Methods::ResourcePatch
Public Class Methods
registered(app)
click to toggle source
# File lib/flapjack/gateways/jsonapi/methods/resource_patch.rb, line 11 def self.registered(app) app.helpers Flapjack::Gateways::JSONAPI::Helpers::Headers app.helpers Flapjack::Gateways::JSONAPI::Helpers::Miscellaneous app.helpers Flapjack::Gateways::JSONAPI::Helpers::Resources app.helpers Flapjack::Gateways::JSONAPI::Helpers::Serialiser Flapjack::Gateways::JSONAPI::RESOURCE_CLASSES.each do |resource_class| jsonapi_method = resource_class.jsonapi_methods[:patch] unless jsonapi_method.nil? jsonapi_links = resource_class.jsonapi_associations singular_links = jsonapi_links.select {|n, jd| jd.send(:patch).is_a?(TrueClass) && :singular.eql?(jd.number) } multiple_links = jsonapi_links.select {|n, jd| jd.send(:post).is_a?(TrueClass) && :multiple.eql?(jd.number) } resource = resource_class.short_model_name.plural app.class_eval do single = resource_class.short_model_name.singular model_type = resource_class.short_model_name.name model_type_plural = model_type.pluralize model_type_update = "#{model_type}Update".to_sym swagger_path "/#{resource}/{#{single}_id}" do operation :patch do key :description, jsonapi_method.descriptions[:singular] key :operationId, "update_#{single}" key :consumes, [JSONAPI_MEDIA_TYPE] parameter do key :name, "#{single}_id".to_sym key :in, :path key :description, "Id of a #{single}" key :required, true key :type, :string key :format, :uuid end parameter do key :name, :data key :in, :body key :description, "#{model_type} to update" key :required, true schema do key :"$ref", model_type_update end end response 204 do key :description, "No Content; #{model_type} update success" end response 403 do key :description, "Forbidden; invalid data" schema do key :'$ref', :Errors end end response 404 do key :description, "Not Found" schema do key :'$ref', :Errors end end response 409 do key :description, "Conflict; request id mismatch" schema do key :'$ref', :Errors end end end end swagger_path "/#{resource}" do operation :patch do key :description, jsonapi_method.descriptions[:multiple] key :operationId, "update_#{resource}" key :consumes, [JSONAPI_MEDIA_TYPE_BULK] parameter do key :name, :data key :in, :body key :description, "#{resource} to update" key :required, true schema do key :type, :array items do key :"$ref", model_type_update end end end response 204 do key :description, "No Content; #{resource} update succeeded" end response 403 do key :description, "Forbidden; invalid data" schema do key :'$ref', :Errors end end response 404 do key :description, "Not Found" schema do key :'$ref', :Errors end end response 409 do key :description, "Conflict; request id mismatch" schema do key :'$ref', :Errors end end end end end app.patch %r{^/#{resource}(?:/(.+))?$} do resource_id = params[:captures].nil? ? nil : params[:captures].first status 204 resources_data, _ = wrapped_params attributes = jsonapi_method.attributes || [] validate_data(resources_data, :attributes => attributes, :singular_links => singular_links, :multiple_links => multiple_links) ids = resources_data.map {|d| d['id']} klasses_to_lock = resources_data.inject([]) do |memo, d| next memo unless d.has_key?('relationships') d['relationships'].each_pair do |k, v| assoc = jsonapi_links[k.to_sym] next if assoc.nil? memo |= assoc.lock_klasses end memo end jsonapi_type = resource_class.short_model_name.singular resource_class.jsonapi_lock_method(:patch, klasses_to_lock) do resources = if resource_id.nil? resources = resource_class.find_by_ids!(*ids) else halt(err(409, "Id path/data mismatch")) unless ids.nil? || ids.eql?([resource_id]) [resource_class.find_by_id!(resource_id)] end resources_by_id = resources.each_with_object({}) {|r, o| o[r.id] = r } attribute_types = resource_class.attribute_types resource_links = resources_data.each_with_object({}) do |d, memo| r = resources_by_id[d['id']] rd = normalise_json_data(attribute_types, d['attributes'] || {}) type = d['type'] halt(err(409, "Resource missing data type")) if type.nil? halt(err(409, "Resource data type '#{type}' does not match endpoint type '#{jsonapi_type}'")) unless jsonapi_type.eql?(type) rd.each_pair do |att, value| next unless attributes.include?(att.to_sym) r.send("#{att}=".to_sym, value) end halt(err(403, "Validation failed, " + r.errors.full_messages.join(', '))) if r.invalid? links = d['relationships'] next if links.nil? singular_links.each_pair do |assoc, assoc_klass| next unless links.has_key?(assoc.to_s) memo[r.id] ||= {} memo[r.id][assoc.to_s] = assoc_klass.data_klass.find_by_id!(links[assoc.to_s]['data']['id']) end multiple_links.each_pair do |assoc, assoc_klass| next unless links.has_key?(assoc.to_s) current_assoc_ids = r.send(assoc.to_sym).ids.to_a memo[r.id] ||= {} link_ids = links[assoc.to_s]['data'].map {|l| l['id']} to_remove = current_assoc_ids - link_ids to_add = link_ids - current_assoc_ids memo[r.id][assoc] = [ to_remove.empty? ? [] : assoc_klass.data_klass.find_by_ids!(*to_remove), to_add.empty? ? [] : assoc_klass.data_klass.find_by_ids!(*to_add) ] end end resources_by_id.each do |r_id, r| r.save! rl = resource_links[r_id] next if rl.nil? rl.each_pair do |assoc, value| case value when Array to_remove = value.first to_add = value.last r.send(assoc.to_sym).remove(*to_remove) unless to_remove.empty? r.send(assoc.to_sym).add(*to_add) unless to_add.empty? else r.send("#{assoc}=".to_sym, value) end end end end true end end end end