module Flexirest::JsonAPIProxy::Response
Parsing JSON API responses
Constants
- ID_PFIX
Public Instance Methods
parse(body, object)
click to toggle source
# File lib/flexirest/json_api_proxy.rb, line 192 def parse(body, object) # Save resource class for building lazy association loaders save_resource_class(object) # According to the spec: # "The members data and errors MUST NOT coexist in the same document." # Thus, if the "errors" key is present, we can return it and ignore the "data" key. return body['errors'] if body.include?('errors') # return early if data is an empty array return [] if body['data'] == [] # Retrieve the resource(s) object or array from the data object records = body['data'] # Convert the resource object to an array, # because it is easier to work with an array than a single object # Also keep track if record is singular or plural for the result later is_singular_record = records.is_a?(Hash) records = [records] if is_singular_record # Retrieve all names of linked relationships relationships = records.first['relationships'] relationships = relationships ? relationships.keys : [] included = body['included'] # Parse the records, and retrieve all resources in a # (nested) array of resources that is easy to work with in Flexirest resources = records.map do |record| fetch_attributes_and_relationships(record, included, relationships) end # Pluck all attributed and associations into hashes resources = resources.map do |resource| pluck_attributes_and_relationships(resource, relationships) end # Depending on whether we got a resource object (hash) or array # in the beginning, return to the caller with the same type is_singular_record ? resources.first : resources end
save_resource_class(object)
click to toggle source
# File lib/flexirest/json_api_proxy.rb, line 188 def save_resource_class(object) @resource_class = object.is_a?(Class) ? object : object.class end
Private Instance Methods
build_lazy_loader(base, relationships, name)
click to toggle source
# File lib/flexirest/json_api_proxy.rb, line 406 def build_lazy_loader(base, relationships, name) is_singular = singular?(name) # Create a new request, given the linked resource `name`, # finding the association's class, and given the `url` to the linked # resource begin # When the response is not a compound document (i.e. there is no # includes object), build a LazyAssociationLoader for lazy loading url = relationships[name]['links']['related'] rescue NoMethodError # If the url for retrieving the linked resource is missing, # we assume there is no linked resource available to fetch # Default nulled linked resource is `nil` or `[]` for resources return is_singular ? nil : [] end klass = find_association_class(base, name) request = Flexirest::Request.new({ url: url, method: :get }, klass.new) # Also add the previous request's header, which may contain # crucial authentication headers (or so), to connect with the service request.headers = @headers request.url = request.forced_url = url Flexirest::LazyAssociationLoader.new(name, url, request) end
fetch_attributes_and_relationships(record, included, rels, base: nil)
click to toggle source
# File lib/flexirest/json_api_proxy.rb, line 237 def fetch_attributes_and_relationships(record, included, rels, base: nil) base = Array(base) unless base.is_a?(Array) rels = rels - [base.last] rels_object = record['relationships'] rels.each do |rel_name| # Determine from `rel_name` (relationship name) whether the # linked resource is a singular or plural (one-to-one or # one-to-many, respectively) is_singular_rel = singular?(rel_name) if is_singular_rel # Fetch a linked resource from the relationships object # and add it as an association attribute in the resource hash record[rel_name], record[ID_PFIX + rel_name], embedded = fetch_one_to_one(base, rels_object, rel_name, included) else # Fetch linked resources from the relationships object # and add it as an array into the resource hash record[rel_name], record[ID_PFIX + rel_name], embedded = fetch_one_to_many(base, rels_object, rel_name, included) end # Do not try to fetch embedded results if the response is not # a compound document. Instead, a LazyAssociationLoader should # have been created and inserted into the record next record unless embedded # Recursively fetch the relationships and embedded nested resources linked_resources = record[rel_name].map do |nested_record| # Find the relationships object in the linked resource # and find whether there are any nested linked resources nested_rels_object = nested_record['relationships'] if nested_rels_object && nested_rels_object.keys.present? # Fetch the linked resources and its attributes recursively fetch_attributes_and_relationships( nested_record, included, nested_rels_object.keys, base: base + [rel_name] ) else nested_record end end record[rel_name] = linked_resources end record end
fetch_one_to_many(base, relationships, name, included)
click to toggle source
# File lib/flexirest/json_api_proxy.rb, line 312 def fetch_one_to_many(base, relationships, name, included) # Parse the relationships object given the relationship name `name`, # and look into the included object (in case of a compound document), # to embed the linked resources into the response if included.blank? || relationships[name]['data'].blank? return build_lazy_loader(base, relationships, name), [], false end # Retrieve the linked resources ids rel_ids = relationships[name]['data'].map { |r| r['id'] } # Index the linked resources' id and types that we need to # retrieve from the included resources relations_to_include = relationships[name]['data'].map { |r| [r['id'], r['type']] }.to_set # Traverse through the included object, and find the included # linked resources, based on the given ids and type name linked_resources = included.select do |i| relations_to_include.include?([i['id'], i['type']]) end return linked_resources, rel_ids, true end
fetch_one_to_one(base, relationships, name, included)
click to toggle source
# File lib/flexirest/json_api_proxy.rb, line 288 def fetch_one_to_one(base, relationships, name, included) # Parse the relationships object given the relationship name `name`, # and look into the included object (in case of a compound document), # to embed the linked resource into the response if included.blank? || relationships[name]['data'].blank? return build_lazy_loader(base, relationships, name), nil, false end # Retrieve the linked resource id and its pluralized type name rel_id = relationships[name]['data']['id'] type_name = relationships[name]['data']['type'] plural_type_name = type_name.pluralize # Traverse through the included object, and find the included # linked resource, based on the given id and pluralized type name linked_resource = included.select do |i| i['id'] == rel_id && i['type'] == plural_type_name end return linked_resource, rel_id, true end
find_association_class(base, name)
click to toggle source
# File lib/flexirest/json_api_proxy.rb, line 388 def find_association_class(base, name) stack = base + [name] klass = @resource_class until stack.empty? shift = stack.shift last = klass klass = klass._associations[shift.underscore.to_sym] if klass.nil? raise "#{last} has no defined relation to #{shift}. " \ "Have you defined :has_one or :has_many :#{shift} in #{last}?" end end klass end
pluck_attributes_and_relationships(record, rels)
click to toggle source
# File lib/flexirest/json_api_proxy.rb, line 336 def pluck_attributes_and_relationships(record, rels) cleaned = { id: record['id'] } relationships = Hash[rels.map { |rel| [rel, singular?(rel)] }] relationships.each do |rel_name, is_singular| safe_name = rel_name.underscore id_sfix = is_singular ? '_id' : '_ids' cleaned[safe_name.singularize + id_sfix] = record[ID_PFIX + rel_name] links = record[rel_name] is_lazy_loader = links.is_a?(Flexirest::LazyAssociationLoader) linked_resources = if is_lazy_loader || links.blank? # Skip this relationship if it hasn't been included links else # Probe the linked resources first_linked = links.first # Retrieve all names of linked relationships nested_rels = if first_linked && first_linked['relationships'] first_linked['relationships'].keys else [] end # Recursively pluck attributes for all related resources links.map do |linked_resource| pluck_attributes_and_relationships(linked_resource, nested_rels) end end # Depending on if the resource is singular or plural, add it as # the original type (array or hash) into the record hash cleaned[safe_name] = if is_lazy_loader || !is_singular linked_resources else linked_resources ? linked_resources.first : nil end end # Fetch attribute keys and values from the resource object # and insert into result record hash record['attributes'].each do |k, v| cleaned[k.underscore] = v end cleaned end