lib/jsonapi/processor.rb in jsonapi-resources-0.9.12 vs lib/jsonapi/processor.rb in jsonapi-resources-0.10.0.beta1
- old
+ new
@@ -15,34 +15,10 @@
:replace_to_many_relationships,
:remove_to_many_relationships,
:remove_to_one_relationship,
:operation
- class << self
- def processor_instance_for(resource_klass, operation_type, params)
- _processor_from_resource_type(resource_klass).new(resource_klass, operation_type, params)
- end
-
- def _processor_from_resource_type(resource_klass)
- processor = resource_klass.name.gsub(/Resource$/,'Processor').safe_constantize
- if processor.nil?
- processor = JSONAPI.configuration.default_processor_klass
- end
-
- return processor
- end
-
- def transactional_operation_type?(operation_type)
- case operation_type
- when :find, :show, :show_related_resource, :show_related_resources
- return false
- else
- return true
- end
- end
- end
-
attr_reader :resource_klass, :operation_type, :params, :context, :result, :result_options
def initialize(resource_klass, operation_type, params)
@resource_klass = resource_klass
@operation_type = operation_type
@@ -67,204 +43,255 @@
filters = params[:filters]
include_directives = params[:include_directives]
sort_criteria = params.fetch(:sort_criteria, [])
paginator = params[:paginator]
fields = params[:fields]
+ serializer = params[:serializer]
verified_filters = resource_klass.verify_filters(filters, context)
+
find_options = {
context: context,
- include_directives: include_directives,
sort_criteria: sort_criteria,
paginator: paginator,
- fields: fields
+ fields: fields,
+ filters: verified_filters,
+ include_directives: include_directives
}
- resource_records = if params[:cache_serializer]
- resource_klass.find_serialized_with_caching(verified_filters,
- params[:cache_serializer],
- find_options)
- else
- resource_klass.find(verified_filters, find_options)
- end
+ resource_set = find_resource_set(resource_klass,
+ include_directives,
+ find_options)
- page_options = {}
- if (JSONAPI.configuration.top_level_meta_include_record_count ||
- (paginator && paginator.class.requires_record_count))
- page_options[:record_count] = resource_klass.find_count(verified_filters,
- context: context,
- include_directives: include_directives)
+ resource_set.populate!(serializer, context, find_options)
+
+ page_options = result_options
+ if (JSONAPI.configuration.top_level_meta_include_record_count || (paginator && paginator.class.requires_record_count))
+ page_options[:record_count] = resource_klass.count(verified_filters,
+ context: context,
+ include_directives: include_directives)
end
- if (JSONAPI.configuration.top_level_meta_include_page_count && page_options[:record_count])
+ if (JSONAPI.configuration.top_level_meta_include_page_count && paginator && page_options[:record_count])
page_options[:page_count] = paginator ? paginator.calculate_page_count(page_options[:record_count]) : 1
end
if JSONAPI.configuration.top_level_links_include_pagination && paginator
- page_options[:pagination_params] = paginator.links_page_params(page_options.merge(fetched_resources: resource_records))
+ page_options[:pagination_params] = paginator.links_page_params(page_options.merge(fetched_resources: resource_set))
end
- return JSONAPI::ResourcesOperationResult.new(:ok, resource_records, page_options)
+ return JSONAPI::ResourcesSetOperationResult.new(:ok, resource_set, page_options)
end
def show
include_directives = params[:include_directives]
fields = params[:fields]
id = params[:id]
+ serializer = params[:serializer]
key = resource_klass.verify_key(id, context)
find_options = {
context: context,
- include_directives: include_directives,
- fields: fields
+ fields: fields,
+ filters: { resource_klass._primary_key => key }
}
- resource_record = if params[:cache_serializer]
- resource_klass.find_by_key_serialized_with_caching(key,
- params[:cache_serializer],
- find_options)
- else
- resource_klass.find_by_key(key, find_options)
- end
+ resource_set = find_resource_set(resource_klass,
+ include_directives,
+ find_options)
- return JSONAPI::ResourceOperationResult.new(:ok, resource_record)
+ resource_set.populate!(serializer, context, find_options)
+
+ return JSONAPI::ResourceSetOperationResult.new(:ok, resource_set, result_options)
end
def show_relationship
parent_key = params[:parent_key]
relationship_type = params[:relationship_type].to_sym
+ paginator = params[:paginator]
+ sort_criteria = params.fetch(:sort_criteria, [])
+ include_directives = params[:include_directives]
+ fields = params[:fields]
parent_resource = resource_klass.find_by_key(parent_key, context: context)
- return JSONAPI::RelationshipOperationResult.new(:ok,
- parent_resource,
- resource_klass._relationship(relationship_type))
+ find_options = {
+ context: context,
+ sort_criteria: sort_criteria,
+ paginator: paginator,
+ fields: fields
+ }
+
+ resource_id_tree = find_related_resource_id_tree(resource_klass,
+ JSONAPI::ResourceIdentity.new(resource_klass, parent_key),
+ relationship_type,
+ find_options,
+ nil)
+
+ return JSONAPI::LinksObjectOperationResult.new(:ok,
+ parent_resource,
+ resource_klass._relationship(relationship_type),
+ resource_id_tree.fragments.keys,
+ result_options)
end
def show_related_resource
+ include_directives = params[:include_directives]
source_klass = params[:source_klass]
source_id = params[:source_id]
- relationship_type = params[:relationship_type].to_sym
+ relationship_type = params[:relationship_type]
+ serializer = params[:serializer]
fields = params[:fields]
- # TODO Should fetch related_resource from cache if caching enabled
+ find_options = {
+ context: context,
+ fields: fields,
+ filters: {}
+ }
+
source_resource = source_klass.find_by_key(source_id, context: context, fields: fields)
- related_resource = source_resource.public_send(relationship_type)
+ resource_set = find_related_resource_set(source_resource,
+ relationship_type,
+ include_directives,
+ find_options)
- return JSONAPI::ResourceOperationResult.new(:ok, related_resource)
+ resource_set.populate!(serializer, context, find_options)
+
+ return JSONAPI::ResourceSetOperationResult.new(:ok, resource_set, result_options)
end
def show_related_resources
source_klass = params[:source_klass]
source_id = params[:source_id]
relationship_type = params[:relationship_type]
filters = params[:filters]
- sort_criteria = params[:sort_criteria]
+ sort_criteria = params.fetch(:sort_criteria, resource_klass.default_sort)
paginator = params[:paginator]
fields = params[:fields]
include_directives = params[:include_directives]
+ serializer = params[:serializer]
- source_resource ||= source_klass.find_by_key(source_id, context: context, fields: fields)
verified_filters = resource_klass.verify_filters(filters, context)
- rel_opts = {
+ find_options = {
filters: verified_filters,
sort_criteria: sort_criteria,
paginator: paginator,
fields: fields,
- context: context,
- include_directives: include_directives
+ context: context
}
- related_resources = nil
- if params[:cache_serializer]
- # TODO Could also avoid instantiating source_resource as actual Resource by
- # allowing LinkBuilder to accept CachedResourceFragment as source in
- # relationships_related_link
- scope = source_resource.public_send(:"records_for_#{relationship_type}", rel_opts)
- relationship = source_klass._relationship(relationship_type)
- related_resources = relationship.resource_klass.find_serialized_with_caching(
- scope,
- params[:cache_serializer],
- rel_opts
- )
- else
- related_resources = source_resource.public_send(relationship_type, rel_opts)
- end
+ source_resource = source_klass.find_by_key(source_id, context: context, fields: fields)
+ resource_set = find_related_resource_set(source_resource,
+ relationship_type,
+ include_directives,
+ find_options)
+
+ resource_set.populate!(serializer, context, find_options)
+
+ opts = result_options
if ((JSONAPI.configuration.top_level_meta_include_record_count) ||
(paginator && paginator.class.requires_record_count) ||
(JSONAPI.configuration.top_level_meta_include_page_count))
- related_resource_records = source_resource.public_send("records_for_" + relationship_type)
- records = resource_klass.filter_records(verified_filters, rel_opts,
- related_resource_records)
- record_count = resource_klass.count_records(records)
+ opts[:record_count] = source_resource.class.count_related(
+ source_resource.identity,
+ relationship_type,
+ find_options)
end
- if (JSONAPI.configuration.top_level_meta_include_page_count && record_count)
- page_count = paginator.calculate_page_count(record_count)
+ if (JSONAPI.configuration.top_level_meta_include_page_count && opts[:record_count])
+ opts[:page_count] = paginator.calculate_page_count(opts[:record_count])
end
- pagination_params = if paginator && JSONAPI.configuration.top_level_links_include_pagination
- page_options = {}
- page_options[:record_count] = record_count if paginator.class.requires_record_count
- paginator.links_page_params(page_options.merge(fetched_resources: related_resources))
- else
- {}
- end
+ opts[:pagination_params] = if paginator && JSONAPI.configuration.top_level_links_include_pagination
+ page_options = {}
+ page_options[:record_count] = opts[:record_count] if paginator.class.requires_record_count
+ paginator.links_page_params(page_options.merge(fetched_resources: resource_set))
+ else
+ {}
+ end
- opts = {}
- opts.merge!(pagination_params: pagination_params) if JSONAPI.configuration.top_level_links_include_pagination
- opts.merge!(record_count: record_count) if JSONAPI.configuration.top_level_meta_include_record_count
- opts.merge!(page_count: page_count) if JSONAPI.configuration.top_level_meta_include_page_count
-
- return JSONAPI::RelatedResourcesOperationResult.new(:ok,
- source_resource,
- relationship_type,
- related_resources,
- opts)
+ return JSONAPI::RelatedResourcesSetOperationResult.new(:ok,
+ source_resource,
+ relationship_type,
+ resource_set,
+ opts)
end
def create_resource
+ include_directives = params[:include_directives]
+ fields = params[:fields]
+ serializer = params[:serializer]
+
data = params[:data]
resource = resource_klass.create(context)
result = resource.replace_fields(data)
- return JSONAPI::ResourceOperationResult.new((result == :completed ? :created : :accepted), resource)
+ find_options = {
+ context: context,
+ fields: fields,
+ filters: { resource_klass._primary_key => resource.id }
+ }
+
+ resource_set = find_resource_set(resource_klass,
+ include_directives,
+ find_options)
+
+ resource_set.populate!(serializer, context, find_options)
+
+ return JSONAPI::ResourceSetOperationResult.new((result == :completed ? :created : :accepted), resource_set, result_options)
end
def remove_resource
resource_id = params[:resource_id]
resource = resource_klass.find_by_key(resource_id, context: context)
result = resource.remove
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
+ return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
end
def replace_fields
resource_id = params[:resource_id]
+ include_directives = params[:include_directives]
+ fields = params[:fields]
+ serializer = params[:serializer]
+
data = params[:data]
resource = resource_klass.find_by_key(resource_id, context: context)
+
result = resource.replace_fields(data)
- return JSONAPI::ResourceOperationResult.new(result == :completed ? :ok : :accepted, resource)
+ find_options = {
+ context: context,
+ fields: fields,
+ filters: { resource_klass._primary_key => resource.id }
+ }
+
+ resource_set = find_resource_set(resource_klass,
+ include_directives,
+ find_options)
+
+ resource_set.populate!(serializer, context, find_options)
+
+ return JSONAPI::ResourceSetOperationResult.new((result == :completed ? :ok : :accepted), resource_set, result_options)
end
def replace_to_one_relationship
resource_id = params[:resource_id]
relationship_type = params[:relationship_type].to_sym
key_value = params[:key_value]
resource = resource_klass.find_by_key(resource_id, context: context)
result = resource.replace_to_one_link(relationship_type, key_value)
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
+ return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
end
def replace_polymorphic_to_one_relationship
resource_id = params[:resource_id]
relationship_type = params[:relationship_type].to_sym
@@ -272,33 +299,33 @@
key_type = params[:key_type]
resource = resource_klass.find_by_key(resource_id, context: context)
result = resource.replace_polymorphic_to_one_link(relationship_type, key_value, key_type)
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
+ return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
end
def create_to_many_relationships
resource_id = params[:resource_id]
relationship_type = params[:relationship_type].to_sym
data = params[:data]
resource = resource_klass.find_by_key(resource_id, context: context)
result = resource.create_to_many_links(relationship_type, data)
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
+ return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
end
def replace_to_many_relationships
resource_id = params[:resource_id]
relationship_type = params[:relationship_type].to_sym
data = params.fetch(:data)
resource = resource_klass.find_by_key(resource_id, context: context)
result = resource.replace_to_many_links(relationship_type, data)
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
+ return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
end
def remove_to_many_relationships
resource_id = params[:resource_id]
relationship_type = params[:relationship_type].to_sym
@@ -311,19 +338,113 @@
result = resource.remove_to_many_link(relationship_type, key)
if complete && result != :completed
complete = false
end
end
- return JSONAPI::OperationResult.new(complete ? :no_content : :accepted)
+ return JSONAPI::OperationResult.new(complete ? :no_content : :accepted, result_options)
end
def remove_to_one_relationship
resource_id = params[:resource_id]
relationship_type = params[:relationship_type].to_sym
resource = resource_klass.find_by_key(resource_id, context: context)
result = resource.remove_to_one_link(relationship_type)
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
+ return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
+ end
+
+ def result_options
+ options = {}
+ options[:warnings] = params[:warnings] if params[:warnings]
+ options
+ end
+
+ def find_resource_set(resource_klass, include_directives, options)
+ include_related = include_directives.include_directives[:include_related] if include_directives
+
+ resource_id_tree = find_resource_id_tree(resource_klass, options, include_related)
+
+ JSONAPI::ResourceSet.new(resource_id_tree)
+ end
+
+ def find_related_resource_set(resource, relationship_name, include_directives, options)
+ include_related = include_directives.include_directives[:include_related] if include_directives
+
+ resource_id_tree = find_resource_id_tree_from_resource_relationship(resource, relationship_name, options, include_related)
+
+ JSONAPI::ResourceSet.new(resource_id_tree)
+ end
+
+ private
+ def find_related_resource_id_tree(resource_klass, source_id, relationship_name, find_options, include_related)
+ options = find_options.except(:include_directives)
+ options[:cache] = resource_klass.caching?
+
+ fragments = resource_klass.find_included_fragments([source_id], relationship_name, options)
+
+ primary_resource_id_tree = PrimaryResourceIdTree.new
+ primary_resource_id_tree.add_resource_fragments(fragments, include_related)
+
+ load_included(resource_klass, primary_resource_id_tree, include_related, options.except(:filters, :sort_criteria))
+
+ primary_resource_id_tree
+ end
+
+ def find_resource_id_tree(resource_klass, find_options, include_related)
+ options = find_options
+ options[:cache] = resource_klass.caching?
+
+ fragments = resource_klass.find_fragments(find_options[:filters], options)
+
+ primary_resource_id_tree = PrimaryResourceIdTree.new
+ primary_resource_id_tree.add_resource_fragments(fragments, include_related)
+
+ load_included(resource_klass, primary_resource_id_tree, include_related, options.except(:filters, :sort_criteria))
+
+ primary_resource_id_tree
+ end
+
+ def find_resource_id_tree_from_resource_relationship(resource, relationship_name, find_options, include_related)
+ relationship = resource.class._relationship(relationship_name)
+
+ options = find_options.except(:include_directives)
+ options[:cache] = relationship.resource_klass.caching?
+
+ fragments = resource.class.find_related_fragments([resource.identity], relationship_name, options)
+
+ primary_resource_id_tree = PrimaryResourceIdTree.new
+ primary_resource_id_tree.add_resource_fragments(fragments, include_related)
+
+ load_included(resource_klass, primary_resource_id_tree, include_related, options.except(:filters, :sort_criteria))
+
+ primary_resource_id_tree
+ end
+
+ def load_included(resource_klass, source_resource_id_tree, include_related, options)
+ source_rids = source_resource_id_tree.fragments.keys
+
+ include_related.try(:each_pair) do |key, value|
+ next unless value[:include]
+ relationship = resource_klass._relationship(key)
+ relationship_name = relationship.name.to_sym
+
+ find_related_resource_options = options.dup
+ find_related_resource_options[:sort_criteria] = relationship.resource_klass.default_sort
+ find_related_resource_options[:cache] = resource_klass.caching?
+
+ related_fragments = resource_klass.find_included_fragments(
+ source_rids, relationship_name, find_related_resource_options
+ )
+
+ related_resource_id_tree = source_resource_id_tree.fetch_related_resource_id_tree(relationship)
+ related_resource_id_tree.add_resource_fragments(related_fragments, include_related[key][include_related])
+
+ # Now recursively get the related resources for the currently found resources
+ load_included(relationship.resource_klass,
+ related_resource_id_tree,
+ include_related[relationship_name][:include_related],
+ options)
+ end
end
end
end