mirror of
https://github.com/ditkrg/schemable.git
synced 2026-01-24 15:16:40 +00:00
108 lines
4.9 KiB
Ruby
108 lines
4.9 KiB
Ruby
module Schemable
|
|
# The IncludedSchemaGenerator class is responsible for generating the 'included' part of a JSON:API compliant response.
|
|
# This class generates schemas for related resources that should be included in the response.
|
|
#
|
|
# @see Schemable
|
|
class IncludedSchemaGenerator
|
|
attr_reader :model_definition, :schema_modifier, :relationships
|
|
|
|
# Initializes a new IncludedSchemaGenerator instance.
|
|
#
|
|
# @param model_definition [ModelDefinition] The model definition to generate the schema for.
|
|
#
|
|
# @example
|
|
# generator = IncludedSchemaGenerator.new(model_definition)
|
|
def initialize(model_definition)
|
|
@model_definition = model_definition
|
|
@schema_modifier = SchemaModifier.new
|
|
@relationships = @model_definition.relationships
|
|
end
|
|
|
|
# Generates the 'included' part of the JSON:API response.
|
|
# It iterates over each relationship type (belongs_to, has_many) and for each relationship,
|
|
# it prepares a schema. If the 'expand' option is true, it also includes the relationships of the related resource in the schema.
|
|
# In that case, the 'addition_to_included' relationships are also included in the schema unless they are excluded from expansion.
|
|
#
|
|
# @param expand [Boolean] Whether to include the relationships of the related resource in the schema.
|
|
# @param relationships_to_exclude_from_expansion [Array] The relationships to exclude from the schema.
|
|
#
|
|
# @note Make sure to provide the names correctly in string format and pluralize them if necessary.
|
|
# For example, if you have a relationship named 'applicant', and an applicant has association
|
|
# named 'identity', you should provide 'identities' as the names of the relationship to exclude from expansion.
|
|
# In this case, the included schema of the applicant will not include the identity relationship.
|
|
#
|
|
# @example
|
|
# schema = generator.generate(expand: true, relationships_to_exclude_from_expansion: ['some_relationship'])
|
|
#
|
|
# @return [Hash] The generated schema.
|
|
def generate(expand: false, relationships_to_exclude_from_expansion: [])
|
|
return {} if @relationships.blank?
|
|
return {} if @relationships == { belongs_to: {}, has_many: {} }
|
|
|
|
definitions = []
|
|
|
|
%i[belongs_to has_many addition_to_included].each do |relation_type|
|
|
next if @relationships[relation_type].blank?
|
|
|
|
definitions << @relationships[relation_type].values
|
|
end
|
|
|
|
definitions.flatten!
|
|
|
|
included_schemas = definitions.map do |definition|
|
|
next if relationships_to_exclude_from_expansion.include?(definition.model_name)
|
|
|
|
if expand
|
|
definition_relations = definition.relationships[:belongs_to].values.map(&:model_name) + definition.relationships[:has_many].values.map(&:model_name)
|
|
relations_to_exclude = []
|
|
definition_relations.each do |relation|
|
|
relations_to_exclude << relation if relationships_to_exclude_from_expansion.include?(relation)
|
|
end
|
|
|
|
prepare_schema_for_included(definition, expand:, relationships_to_exclude_from_expansion: relations_to_exclude)
|
|
else
|
|
prepare_schema_for_included(definition)
|
|
end
|
|
end
|
|
|
|
schema = {
|
|
included: {
|
|
type: :array,
|
|
items: {
|
|
anyOf: included_schemas.compact_blank
|
|
}
|
|
}
|
|
}
|
|
|
|
@schema_modifier.add_properties(schema, @model_definition.additional_response_included, 'included.items') if @model_definition.additional_response_included.present?
|
|
|
|
schema
|
|
end
|
|
|
|
# Prepares the schema for a related resource to be included in the response.
|
|
# It generates the attribute and relationship schemas for the related resource and combines them into a single schema.
|
|
#
|
|
# @param model_definition [ModelDefinition] The model definition of the related resource.
|
|
# @param expand [Boolean] Whether to include the relationships of the related resource in the schema.
|
|
# @param relationships_to_exclude_from_expansion [Array] The relationships to exclude from the schema.
|
|
#
|
|
# @example
|
|
# included_schema = generator.prepare_schema_for_included(related_model_definition, expand: true, relationships_to_exclude_from_expansion: ['some_relationship'])
|
|
#
|
|
# @return [Hash] The generated schema for the related resource.
|
|
def prepare_schema_for_included(model_definition, expand: false, relationships_to_exclude_from_expansion: [])
|
|
attributes_schema = AttributeSchemaGenerator.new(model_definition).generate
|
|
relationships_schema = RelationshipSchemaGenerator.new(model_definition).generate(relationships_to_exclude_from_expansion:, expand:)
|
|
|
|
{
|
|
type: :object,
|
|
properties: {
|
|
type: { type: :string, default: model_definition.model_name },
|
|
id: { type: :string },
|
|
attributes: attributes_schema
|
|
}.merge!(relationships_schema.blank? ? {} : { relationships: relationships_schema })
|
|
}.compact_blank
|
|
end
|
|
end
|
|
end
|