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
1 change: 1 addition & 0 deletions lib/reynard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# OpenAPI specification.
class Reynard
extend Forwardable

def_delegators :build_context, :logger, :base_url, :operation, :headers, :params
def_delegators :@specification, :servers
def_delegators :@inflector, :snake_cases
Expand Down
1 change: 1 addition & 0 deletions lib/reynard/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class Reynard
# Exposes a public interface to build a request context.
class Context
extend Forwardable

def_delegators :@request_context, :verb, :path, :full_path, :url

def initialize(specification:, inflector:, request_context: nil)
Expand Down
1 change: 1 addition & 0 deletions lib/reynard/http/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class Http
# in the specification.
class Response
extend Forwardable

def_delegators :@http_response, :code, :content_type, :[], :body

def initialize(specification:, inflector:, request_context:, http_response:)
Expand Down
1 change: 1 addition & 0 deletions lib/reynard/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class Reynard
# Superclass for dynamic classes generated by the object builder.
class Model < BasicObject
extend ::Forwardable

def_delegators :@attributes, :[], :empty?

class << self
Expand Down
11 changes: 10 additions & 1 deletion lib/reynard/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def initialize(specification:, node:, namespace: nil)
def type
return @type if defined?(@type)

@type = @specification.dig(*node, 'type')
schema = @specification.dig(*node)
@type = schema ? self.class.determine_schema_type(schema) : nil
end

def model_name
Expand Down Expand Up @@ -46,6 +47,14 @@ def property_schema(name)
)
end

def self.determine_schema_type(schema)
if schema.key?('type')
schema['type']
elsif schema.keys.intersect?(%w[allOf anyOf oneOf])
'object'
end
end

private

def model_naming
Expand Down
105 changes: 105 additions & 0 deletions test/files/openapi/polymorphic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
openapi: "3.1.0"
info:
title: Pets
version: 1.0.0
contact: {}
description: Find out about my pet.
servers:
- url: http://example.com
tags:
- name: pets
description: Animals in custody of a person.
paths:
/pets:
get:
summary: List all pets
description: A list of all my pets.
operationId: getPets
tags:
- pets
responses:
"200":
description: Array of pets
content:
application/json:
schema:
type: array
items:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
/pets/{id}:
get:
summary: Get pet
description: A description of the pet
operationId: getPet
tags:
- pets
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
"200":
description: A pet
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
discriminator:
propertyName: pet_type
/status:
get:
summary: Get status of the service
description: A status object for the service
operationId: getStatus
tags:
- pets
responses:
"200":
description: A status
content:
application/json:
schema:
anyOf:
- type: object
properties:
status:
type: string
- type: object
properties:
description:
type: string
- type: object
properties:
status:
type: integer
components:
schemas:
Pet:
type: object
required:
- pet_type
properties:
pet_type:
type: string
discriminator:
propertyName: pet_type
Cat:
allOf:
- $ref: "#/components/schemas/Pet"
- type: object
properties:
red:
type: boolean
Dog:
allOf:
- $ref: "#/components/schemas/Pet"
- type: object
properties:
tial:
type: boolean
50 changes: 50 additions & 0 deletions test/reynard/object_builder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -305,4 +305,54 @@ def setup
assert_model_name('NationalIndustry', national_industry)
end
end

class PolymorphicBuilderTest < Reynard::Test
Response = Struct.new(:body, keyword_init: true)

def setup
@specification = Specification.new(filename: fixture_file('openapi/polymorphic.yml'))
@inflector = Inflector.new
end

test 'handles oneOf and allOf' do
operation = @specification.operation('getPets')
media_type = @specification.media_type(operation.node, '200', 'application/json')
schema = @specification.schema(media_type.node)
collection = Reynard::ObjectBuilder.new(
schema:,
inflector: @inflector,
parsed_body: [
{
'pet_type' => 'Dog',
'tail' => true
}
]
).call

assert_equal(1, collection.size)
record = collection[0]

assert_model_name('Pet', record)
assert_equal 'Dog', record.pet_type
assert record.tail
end

test 'handles anyOf' do
operation = @specification.operation('getStatus')
media_type = @specification.media_type(operation.node, '200', 'application/json')
schema = @specification.schema(media_type.node)
record = Reynard::ObjectBuilder.new(
schema:,
inflector: @inflector,
parsed_body: {
'status' => 'online',
'description' => 'Everything works'
}
).call

assert_model_name('Statu', record)
assert_equal 'online', record.status
assert_equal 'Everything works', record.description
end
end
end
3 changes: 2 additions & 1 deletion test/reynard/schema/model_naming_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class RegressionModelNamingTest < Reynard::Test
'weird' => %w[
HowdyPardner AFRootWithInThe Fugol Bird Duckbill Duckbill HowdyPardner
FugolCollection BirdsCollection DuckbillCollection
]
],
'polymorphic' => %w[Pet PetCollection]
}.freeze

test 'produces a model name for every schema node in every specification' do
Expand Down
23 changes: 23 additions & 0 deletions test/reynard/schema_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@

class Reynard
class SchemaTest < Reynard::Test
test 'uses object schema type for object schemas' do
assert_equal('object', Schema.determine_schema_type({ 'type' => 'object' }))
end

test 'uses array schema type for array schemas' do
assert_equal('array', Schema.determine_schema_type({ 'type' => 'array' }))
end

test 'uses object schema type for allOf' do
assert_equal('object', Schema.determine_schema_type({ 'allOf' => {} }))
end

test 'uses object schema type for anyOf' do
assert_equal('object', Schema.determine_schema_type({ 'anyOf' => {} }))
end

test 'uses object schema type for oneOf' do
assert_equal('object', Schema.determine_schema_type({ 'oneOf' => {} }))
end

test 'does not return a schema type for unsupported schemas' do
assert_nil Schema.determine_schema_type({})
end
end

class SingularTopLevelSchemaTest < Reynard::Test
Expand Down