Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions lib/active_model/entity/schemas/json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,29 @@ def enum_attributes
end
end

def json_schema_attribute_for(type)
def primitive_type_schema(type)
return { type: :object } if type.type.nil?
return { type: :number } if NUMBER_TYPES.include?(type.type)
return { type: :string } if STRING_TYPES.include?(type.type)
return { type: :boolean } if BOOLEAN_TYPES.include?(type.type)
return { "$ref": type.entity_type.json_schema_ref } if type.is_a?(Type::Entity)
return { items: json_schema_attribute_for(type.element_type), type: :array } if type.is_a?(Type::Array)

nil
end

def entity_schema_for(type, inline)
inline ? type.entity_type.as_json_schema(inline:) : { "$ref": type.entity_type.json_schema_ref }
end

def array_schema_for(type, inline)
{ items: json_schema_attribute_for(type.element_type, inline:), type: :array }
end

def json_schema_attribute_for(type, inline: false)
schema = primitive_type_schema(type)
return schema if schema

return entity_schema_for(type, inline) if type.is_a?(Type::Entity)
return array_schema_for(type, inline) if type.is_a?(Type::Array)

raise NotImplementedError
end
Expand All @@ -68,14 +84,14 @@ def append_enum!(values, options)
options[:enum] = values
end

def as_json_schema
def as_json_schema(inline: false)
type = :object
description = meta_descriptions[nil].first
required = required_attributes.map(&:name).map { _1.camelize(:lower) }
nullable = nullable_attributes.map(&:name).index_by { _1.camelize(:lower) }

attributes = attribute_types.transform_keys { _1.camelize(:lower) }
properties = attributes.transform_values { json_schema_attribute_for(_1) }
properties = attributes.transform_values { json_schema_attribute_for(_1, inline:) }
enums = enum_attributes.transform_keys { _1.camelize(:lower) }

properties.each do |name, options|
Expand Down
53 changes: 53 additions & 0 deletions spec/active_model/entity/schemas/json_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,57 @@ class Person
properties:
})
end

context "with inline: true" do
it "inlines nested entity schemas" do
schema = SchemasTest::Person.as_json_schema(inline: true)

expect(schema[:properties]["fieldRole"]).to eq({
type: :object,
required: [],
properties: { "fieldName" => { type: :string } }
})
end

it "inlines array of entity schemas" do
schema = SchemasTest::Person.as_json_schema(inline: true)

expect(schema[:properties]["fieldRoles"]).to eq({
type: :array,
items: {
type: :object,
required: [],
properties: { "fieldName" => { type: :string } }
}
})
end

it "preserves nullable flag on inlined entities" do
schema = SchemasTest::Person.as_json_schema(inline: true)

expect(schema[:properties]["fieldNullableRole"]).to eq({
type: :object,
required: [],
properties: { "fieldName" => { type: :string } },
nullable: true
})
end

it "keeps primitive types unchanged" do
schema = SchemasTest::Person.as_json_schema(inline: true)

expect(schema[:properties]["fieldString"]).to eq({ type: :string })
expect(schema[:properties]["fieldInteger"]).to eq({ type: :number })
expect(schema[:properties]["fieldBoolean"]).to eq({ type: :boolean })
end

it "keeps array of primitives unchanged" do
schema = SchemasTest::Person.as_json_schema(inline: true)

expect(schema[:properties]["fieldIntegers"]).to eq({
type: :array,
items: { type: :number }
})
end
end
end