Created
May 20, 2020 09:54
-
-
Save balakirevs/806bc537215f13e382ebe34f0295483d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SidePostingActiveRecordOperationsProcessor < ActiveRecordOperationsProcessor | |
define_jsonapi_resources_callbacks :create_included_resource_operation | |
after_create_resource_operation :append_create_sideposted_to_one_resource_operations | |
before_create_resource_operation :append_create_sideposted_to_many_resources_operations | |
before_replace_fields_operation :append_create_sideposted_to_many_resources_operations | |
def append_create_sideposted_to_one_resource_operations | |
return if @result.is_a?(JSONAPI::ErrorsOperationResult) | |
@operations.each do |operation| | |
resource_klass = operation.resource_klass | |
operation.data[:to_one].each do |relationship_name, id| | |
relationship = resource_klass._relationships[relationship_name] | |
next unless relationship | |
sideposted_resource = sideposted_resource(relationship, id) | |
next unless sideposted_resource | |
op = create_sideposted_resource_operation(operation, relationship, sideposted_resource) | |
@operations << op unless operations_contain?(op) | |
end | |
end | |
end | |
def append_create_sideposted_to_many_resources_operations | |
return if @result.is_a?(JSONAPI::ErrorsOperationResult) | |
@operations.each do |operation| | |
resource_klass = operation.resource_klass | |
id_store = {} | |
operation.data[:to_many].each do |relationship_name, ids| | |
id_store[relationship_name.to_sym] = ids.dup | |
end | |
operation.data[:to_many].each do |relationship_name, ids| | |
relationship = resource_klass._relationships[relationship_name] | |
next unless relationship | |
sideposted_resources = sideposted_resources(relationship, id_store[relationship_name.to_sym]) | |
next unless sideposted_resources.any? | |
sideposted_resources.each do |sideposted_resource| | |
op = create_sideposted_resource_operation(operation, relationship, sideposted_resource) | |
@operations << op unless operations_contain?(op) | |
end | |
end | |
end | |
end | |
def sideposted_resource(relationship, id) | |
included = @request.params[:included] | |
return nil unless included | |
type = relationship.type.to_s.dasherize | |
included.each_with_index do |resource_object, index| | |
if resource_object['type'] == type && resource_object['id'] == id | |
return { | |
index: index, | |
resource_object: resource_object | |
} | |
end | |
end | |
return nil | |
end | |
def sideposted_resources(relationship, ids) | |
ids.map do |id| | |
sideposted_resource(relationship, id) | |
end.select(&:present?) | |
end | |
def create_sideposted_resource_operation(operation, resource_relationship, sideposted_resource) | |
resource_object = sideposted_resource[:resource_object] | |
attributes = { | |
'id' => resource_object['id'] | |
} | |
resource_object['attributes'].each do |key, value| | |
attributes[@request.unformat_key(key)] = value | |
end | |
to_one = {} | |
to_many = {} | |
(resource_object['relationships'] || {}).each do |relationship_name, relationship_data| | |
next if relationship_data['data'].nil? | |
relationship_name = @request.unformat_key(relationship_name) | |
relationship = resource_relationship.resource_klass._relationships[relationship_name] | |
case relationship | |
when JSONAPI::Relationship::ToOne | |
to_one[relationship_name] = relationship_data['data']['id'] | |
end | |
end | |
operation.data[:to_many].each do |_relationship_name, relationship_ids| | |
relationship_ids.delete_if { |id| id =~ /\A_/ } | |
end | |
CreateIncludedResourceOperation.new( | |
resource_relationship.resource_klass, | |
context: @request.context, | |
parent_operation: operation, | |
operations: @operations, | |
relationship: resource_relationship, | |
index: sideposted_resource[:index], | |
data: { | |
attributes: attributes, | |
to_one: to_one, | |
to_many: to_many | |
} | |
) | |
end | |
def operations_contain?(operation) | |
@operations.any? do |op| | |
op.resource_klass == operation.resource_klass && op.data == operation.data | |
end | |
end | |
class CreateIncludedResourceOperation < JSONAPI::CreateResourceOperation | |
attr_reader :parent_operation, :relationship, :operations, :index | |
def initialize(resource_klass, options = {}) | |
super | |
@parent_operation = options[:parent_operation] | |
@relationship = options[:relationship] | |
@operations = options[:operations] | |
@index = options[:index] | |
end | |
def apply | |
relation_name = relationship.relation_name(@context) | |
parent_resource = parent_operation.result.resource | |
data[:to_one].each do |relationship_name, id| | |
operation = operations.find { |o| o.result.try(:resource).try(:client_id) == id } | |
next unless operation | |
data[:to_one][relationship_name] = operation.result.resource.id | |
end | |
resource = | |
if parent_resource._model.respond_to?("build_#{relation_name}") | |
model = parent_resource._model.send("build_#{relation_name}") | |
@resource_klass.new(model, @context) | |
else | |
@resource_klass.create(@context) | |
end | |
result = resource.replace_fields(@data) | |
if result == :completed && parent_resource.respond_to?("#{relationship.name}=") | |
parent_resource.send("#{relationship.name}=", resource._model) | |
end | |
@result = JSONAPI::ResourceOperationResult.new((result == :completed ? :created : :accepted), resource) | |
rescue JSONAPI::Exceptions::Error => exception | |
error_code = nil | |
errors = exception.errors.map do |error| | |
error_code ||= error.code | |
if error.source.try(:[], :pointer) | |
error.source[:pointer] = error.source[:pointer].sub('/data/', "/included/#{index}/") | |
end | |
error | |
end | |
JSONAPI::ErrorsOperationResult.new(error_code, errors) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment