Skip to content

Commit

Permalink
Merge pull request #50 from MaximeRDY/features/inheritance_and_discri…
Browse files Browse the repository at this point in the history
…minator

Features/inheritance and discriminator
  • Loading branch information
kzaitsev authored Jun 28, 2020
2 parents dfd2eb0 + 0422d68 commit 7f290cd
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 26 deletions.
5 changes: 1 addition & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
#### Features

* Your contribution here.

#### Features

* Your contribution here.
* [#50](https://github.com/ruby-grape/grape-swagger-entity/pull/50): Features/inheritance and discriminator - [@MaximeRDY](https://github.com/MaximeRDY).

### 0.4.0 (May 30, 2020)

Expand Down
1 change: 1 addition & 0 deletions lib/grape-swagger/entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'grape-entity'

require 'grape-swagger/entity/version'
require 'grape-swagger/entity/helper'
require 'grape-swagger/entity/attribute_parser'
require 'grape-swagger/entity/parser'

Expand Down
8 changes: 6 additions & 2 deletions lib/grape-swagger/entity/attribute_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def call(entity_options)
entity_model = model_from(entity_options)

if entity_model
name = endpoint.nil? ? entity_model.to_s.demodulize : endpoint.send(:expose_params_from_model, entity_model)
name = GrapeSwagger::Entity::Helper.model_name(entity_model, endpoint)

entity_model_type = entity_model_type(name, entity_options)
return entity_model_type unless documentation
Expand All @@ -39,7 +39,7 @@ def call(entity_options)
add_attribute_documentation(param, documentation)

add_extension_documentation(param, documentation)

add_discriminator_extension(param, documentation)
param
end
end
Expand Down Expand Up @@ -128,6 +128,10 @@ def add_array_documentation(param, documentation)
def add_extension_documentation(param, documentation)
GrapeSwagger::DocMethods::Extensions.add_extensions_to_root(documentation, param)
end

def add_discriminator_extension(param, documentation)
param[:documentation] = { is_discriminator: true } if documentation.key?(:is_discriminator)
end
end
end
end
36 changes: 36 additions & 0 deletions lib/grape-swagger/entity/helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module GrapeSwagger
module Entity
# Helper methods for DRY
class Helper
class << self
def model_name(entity_model, endpoint)
if endpoint.nil?
entity_model.to_s.demodulize
else
endpoint.send(:expose_params_from_model, entity_model)
end
end

def discriminator(entity_model)
entity_model.superclass.root_exposures.detect do |value|
value.documentation.dig(:is_discriminator)
end
end

def root_exposures_without_parent(entity_model)
entity_model.root_exposures.select do |value|
entity_model.superclass.root_exposures.find_by(value.attribute).nil?
end
end

def root_exposure_with_discriminator(entity_model)
if discriminator(entity_model)
root_exposures_without_parent(entity_model)
else
entity_model.root_exposures
end
end
end
end
end
end
36 changes: 34 additions & 2 deletions lib/grape-swagger/entity/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def initialize(original, renamed)
end

def extract_params(exposure)
exposure.root_exposures.each_with_object({}) do |value, memo|
GrapeSwagger::Entity::Helper.root_exposure_with_discriminator(exposure).each_with_object({}) do |value, memo|
if value.for_merge && (value.respond_to?(:entity_class) || value.respond_to?(:using_class_name))
entity_class = value.respond_to?(:entity_class) ? value.entity_class : value.using_class_name

Expand Down Expand Up @@ -64,7 +64,39 @@ def parse_grape_entity_params(params, parent_model = nil)
memo[final_entity_name][:description] = documentation[:desc] if documentation[:desc]
end

[parsed, required_params(params)]
discriminator = GrapeSwagger::Entity::Helper.discriminator(model)
if discriminator
respond_with_all_of(parsed, params, discriminator)
else
[parsed, required_params(params)]
end
end

def respond_with_all_of(parsed, params, discriminator)
parent_name = GrapeSwagger::Entity::Helper.model_name(model.superclass, endpoint)

{
allOf: [
{
'$ref' => "#/definitions/#{parent_name}"
},
[
add_discriminator(parsed, discriminator),
required_params(params).push(discriminator.attribute)
]
]
}
end

def add_discriminator(parsed, discriminator)
model_name = GrapeSwagger::Entity::Helper.model_name(model, endpoint)

parsed.merge(
discriminator.attribute => {
type: 'string',
enum: [model_name]
}
)
end

def parse_nested(entity_name, entity_options, parent_model = nil)
Expand Down
79 changes: 61 additions & 18 deletions spec/grape-swagger/entity/parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,74 @@
require_relative '../../../spec/support/shared_contexts/this_api'

describe GrapeSwagger::Entity::Parser do
include_context 'this api'
context 'this api' do
include_context 'this api'

describe '#call' do
let(:parsed_entity) { described_class.new(ThisApi::Entities::Something, endpoint).call }
let(:properties) { parsed_entity.first }
let(:required) { parsed_entity.last }
describe '#call' do
let(:parsed_entity) { described_class.new(ThisApi::Entities::Something, endpoint).call }
let(:properties) { parsed_entity.first }
let(:required) { parsed_entity.last }

context 'when no endpoint is passed' do
let(:endpoint) { nil }
context 'when no endpoint is passed' do
let(:endpoint) { nil }

it 'parses the model with the correct :using definition' do
expect(properties[:kind]['$ref']).to eq('#/definitions/Kind')
expect(properties[:kind2]['$ref']).to eq('#/definitions/Kind')
expect(properties[:kind3]['$ref']).to eq('#/definitions/Kind')
it 'parses the model with the correct :using definition' do
expect(properties[:kind]['$ref']).to eq('#/definitions/Kind')
expect(properties[:kind2]['$ref']).to eq('#/definitions/Kind')
expect(properties[:kind3]['$ref']).to eq('#/definitions/Kind')
end

it 'merges attributes that have merge: true defined' do
expect(properties[:merged_attribute]).to be_nil
expect(properties[:code][:type]).to eq('string')
expect(properties[:message][:type]).to eq('string')
expect(properties[:attr][:type]).to eq('string')
end

it 'hides hidden attributes' do
expect(properties).to_not include(:hidden_attr)
end
end
end
end
context 'inheritance api' do
include_context 'inheritance api'

it 'merges attributes that have merge: true defined' do
expect(properties[:merged_attribute]).to be_nil
expect(properties[:code][:type]).to eq('string')
expect(properties[:message][:type]).to eq('string')
expect(properties[:attr][:type]).to eq('string')
describe '#call for Parent' do
let(:parsed_entity) do
described_class.new(InheritanceApi::Entities::Parent, endpoint).call
end
let(:properties) { parsed_entity.first }

context 'when no endpoint is passed' do
let(:endpoint) { nil }

it 'parses the model with discriminator' do
expect(properties[:type][:documentation]).to eq(is_discriminator: true)
end
end
end

describe '#call for Child' do
let(:parsed_entity) do
described_class.new(InheritanceApi::Entities::Child, endpoint).call
end
let(:properties) { parsed_entity }

context 'when no endpoint is passed' do
let(:endpoint) { nil }

it 'hides hidden attributes' do
expect(properties).to_not include(:hidden_attr)
it 'parses the model with allOf' do
expect(properties).to include(:allOf)
all_of = properties[:allOf]
child_property = all_of.last.first
child_required = all_of.last.last
expect(all_of.first['$ref']).to eq('#/definitions/Parent')
expect(child_property[:name][:type]).to eq('string')
expect(child_property[:type][:type]).to eq('string')
expect(child_property[:type][:enum]).to eq(['Child'])
expect(child_required).to include(:type)
end
end
end
end
Expand Down
16 changes: 16 additions & 0 deletions spec/support/shared_contexts/inheritance_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
shared_context 'inheritance api' do
before :all do
module InheritanceApi
module Entities
class Parent < Grape::Entity
expose :type, documentation: { type: 'string', is_discriminator: true, required: true }
expose :id, documentation: { type: 'integer' }
end

class Child < Parent
expose :name, documentation: { type: 'string', desc: 'Name' }
end
end
end
end
end

0 comments on commit 7f290cd

Please sign in to comment.