Skip to content

Commit 289df4c

Browse files
committed
Add inheritance and discriminator attributes
1 parent dfd2eb0 commit 289df4c

5 files changed

Lines changed: 118 additions & 21 deletions

File tree

lib/grape-swagger/entity/attribute_parser.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def call(entity_options)
3939
add_attribute_documentation(param, documentation)
4040

4141
add_extension_documentation(param, documentation)
42-
42+
add_discriminator_extension(param, documentation)
4343
param
4444
end
4545
end
@@ -128,6 +128,10 @@ def add_array_documentation(param, documentation)
128128
def add_extension_documentation(param, documentation)
129129
GrapeSwagger::DocMethods::Extensions.add_extensions_to_root(documentation, param)
130130
end
131+
132+
def add_discriminator_extension(param, documentation)
133+
param[:documentation] = { is_discriminator: true } if documentation.key?(:is_discriminator)
134+
end
131135
end
132136
end
133137
end

lib/grape-swagger/entity/parser.rb

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ def initialize(original, renamed)
2626
end
2727

2828
def extract_params(exposure)
29-
exposure.root_exposures.each_with_object({}) do |value, memo|
29+
root_exposures =
30+
if superclass_contains_discriminator?(exposure)
31+
root_exposures_without_parent(exposure)
32+
else
33+
exposure.root_exposures
34+
end
35+
root_exposures.each_with_object({}) do |value, memo|
3036
if value.for_merge && (value.respond_to?(:entity_class) || value.respond_to?(:using_class_name))
3137
entity_class = value.respond_to?(:entity_class) ? value.entity_class : value.using_class_name
3238

@@ -39,6 +45,18 @@ def extract_params(exposure)
3945
end
4046
end
4147

48+
def superclass_contains_discriminator?(exposure)
49+
exposure.superclass.root_exposures.detect do |value|
50+
value.documentation[:is_discriminator]
51+
end
52+
end
53+
54+
def root_exposures_without_parent(exposure)
55+
exposure.root_exposures.select do |value|
56+
exposure.superclass.root_exposures.find_by(value.attribute).nil?
57+
end
58+
end
59+
4260
def parse_grape_entity_params(params, parent_model = nil)
4361
return unless params
4462

@@ -63,8 +81,28 @@ def parse_grape_entity_params(params, parent_model = nil)
6381
memo[final_entity_name][:readOnly] = documentation[:read_only].to_s == 'true' if documentation[:read_only]
6482
memo[final_entity_name][:description] = documentation[:desc] if documentation[:desc]
6583
end
84+
if superclass_contains_discriminator?(model)
85+
respond_with_all_of(parsed, params)
86+
else
87+
[parsed, required_params(params)]
88+
end
89+
end
6690

67-
[parsed, required_params(params)]
91+
def respond_with_all_of(parsed, params)
92+
parent_name =
93+
if endpoint.nil?
94+
model.superclass.to_s.demodulize
95+
else
96+
endpoint.send(:expose_params_from_model, model.superclass)
97+
end
98+
{
99+
allOf: [
100+
{
101+
'$ref' => "#/definitions/#{parent_name}"
102+
},
103+
[parsed, required_params(params)]
104+
]
105+
}
68106
end
69107

70108
def parse_nested(entity_name, entity_options, parent_model = nil)

spec/grape-swagger/entity/parser_spec.rb

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,69 @@
22
require_relative '../../../spec/support/shared_contexts/this_api'
33

44
describe GrapeSwagger::Entity::Parser do
5-
include_context 'this api'
5+
context 'this api' do
6+
include_context 'this api'
67

7-
describe '#call' do
8-
let(:parsed_entity) { described_class.new(ThisApi::Entities::Something, endpoint).call }
9-
let(:properties) { parsed_entity.first }
10-
let(:required) { parsed_entity.last }
8+
describe '#call' do
9+
let(:parsed_entity) { described_class.new(ThisApi::Entities::Something, endpoint).call }
10+
let(:properties) { parsed_entity.first }
11+
let(:required) { parsed_entity.last }
1112

12-
context 'when no endpoint is passed' do
13-
let(:endpoint) { nil }
13+
context 'when no endpoint is passed' do
14+
let(:endpoint) { nil }
1415

15-
it 'parses the model with the correct :using definition' do
16-
expect(properties[:kind]['$ref']).to eq('#/definitions/Kind')
17-
expect(properties[:kind2]['$ref']).to eq('#/definitions/Kind')
18-
expect(properties[:kind3]['$ref']).to eq('#/definitions/Kind')
16+
it 'parses the model with the correct :using definition' do
17+
expect(properties[:kind]['$ref']).to eq('#/definitions/Kind')
18+
expect(properties[:kind2]['$ref']).to eq('#/definitions/Kind')
19+
expect(properties[:kind3]['$ref']).to eq('#/definitions/Kind')
20+
end
21+
22+
it 'merges attributes that have merge: true defined' do
23+
expect(properties[:merged_attribute]).to be_nil
24+
expect(properties[:code][:type]).to eq('string')
25+
expect(properties[:message][:type]).to eq('string')
26+
expect(properties[:attr][:type]).to eq('string')
27+
end
28+
29+
it 'hides hidden attributes' do
30+
expect(properties).to_not include(:hidden_attr)
31+
end
1932
end
33+
end
34+
end
35+
context 'inheritance api' do
36+
include_context 'inheritance api'
2037

21-
it 'merges attributes that have merge: true defined' do
22-
expect(properties[:merged_attribute]).to be_nil
23-
expect(properties[:code][:type]).to eq('string')
24-
expect(properties[:message][:type]).to eq('string')
25-
expect(properties[:attr][:type]).to eq('string')
38+
describe '#call for Parent' do
39+
let(:parsed_entity) do
40+
described_class.new(InheritanceApi::Entities::Parent, endpoint).call
2641
end
42+
let(:properties) { parsed_entity.first }
43+
44+
context 'when no endpoint is passed' do
45+
let(:endpoint) { nil }
46+
47+
it 'parses the model with discriminator' do
48+
expect(properties[:type][:documentation]).to eq(is_discriminator: true)
49+
end
50+
end
51+
end
52+
53+
describe '#call for Child' do
54+
let(:parsed_entity) do
55+
described_class.new(InheritanceApi::Entities::Child, endpoint).call
56+
end
57+
let(:properties) { parsed_entity }
58+
59+
context 'when no endpoint is passed' do
60+
let(:endpoint) { nil }
2761

28-
it 'hides hidden attributes' do
29-
expect(properties).to_not include(:hidden_attr)
62+
it 'parses the model with allOf' do
63+
expect(properties).to include(:allOf)
64+
all_of = properties[:allOf]
65+
expect(all_of.first['$ref']).to eq('#/definitions/Parent')
66+
expect(all_of.last.first[:name][:type]).to eq('string')
67+
end
3068
end
3169
end
3270
end

spec/spec_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
require 'rack'
99
require 'rack/test'
10+
require 'pry'
1011

1112
RSpec.configure do |config|
1213
require 'rspec/expectations'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
shared_context 'inheritance api' do
2+
before :all do
3+
module InheritanceApi
4+
module Entities
5+
class Parent < Grape::Entity
6+
expose :type, documentation: { type: 'string', is_discriminator: true, required: true }
7+
expose :id, documentation: { type: 'integer' }
8+
end
9+
10+
class Child < Parent
11+
expose :name, documentation: { type: 'string', desc: 'Name' }
12+
end
13+
end
14+
end
15+
end
16+
end

0 commit comments

Comments
 (0)