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 .rspec
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
--color
--profile
--format documentation
--require 'spec_helper'
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* [#958](https://github.com/ruby-grape/grape-swagger/pull/958): Drop ruby-head from test matrix - [@numbata](https://github.com/numbata).
* [#953](https://github.com/ruby-grape/grape-swagger/pull/953): Added `super_diff` - [@numbata](https://github.com/numbata).
* [#951](https://github.com/ruby-grape/grape-swagger/pull/951): Use `x-example` for non-body parameters - [@olivier-thatch](https://github.com/olivier-thatch).
* [#963](https://github.com/ruby-grape/grape-swagger/pull/963): Allow empty model definitions for swagger 2.0 - [@numbata](https://github.com/numbata).
* Your contribution here.

#### Fixes
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

source 'http://rubygems.org'
source 'https://rubygems.org'

gemspec

Expand Down
2 changes: 1 addition & 1 deletion lib/grape-swagger/doc_methods/build_model_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def parse_params_from_model(parsed_response, model, model_name)
}
else
properties, required = parsed_response
unless properties&.any?
if properties.nil?
raise GrapeSwagger::Errors::SwaggerSpec,
"Empty model #{model_name}, swagger 2.0 doesn't support empty definitions."
end
Expand Down
3 changes: 2 additions & 1 deletion spec/issues/539_array_post_body_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ class ArrayOfElements < Grape::Entity
'id' => { 'type' => 'string' },
'description' => { 'type' => 'string' },
'role' => { 'type' => 'string' }
}
},
'required' => %w[id description role]
}
)
end
Expand Down
110 changes: 110 additions & 0 deletions spec/issues/962_polymorphic_entity_with_custom_documentation_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# frozen_string_literal: true

describe '#962 polymorphic entity with custom documentation' do
let(:app) do
Class.new(Grape::API) do
namespace :issue_962 do
module Issue962
class EmptyEntity < Grape::Entity
end

class EntityWithHiddenProperty < Grape::Entity
expose :hidden_prop, documentation: { hidden: true, desc: 'This property is not exposed.' }
end

class EntityWithNestedEmptyEntity < Grape::Entity
expose :array_of_empty_entities,
as: :empty_items,
using: Issue962::EmptyEntity,
documentation: {
is_array: true,
desc: 'This is a nested empty entity.'
}
expose :array_of_hidden_entities,
as: :hidden_items,
using: Issue962::EntityWithHiddenProperty,
documentation: {
is_array: true,
desc: 'This is a nested entity with hidden props'
}
end
end

desc 'Get a report',
success: Issue962::EntityWithNestedEmptyEntity
get '/' do
present({ foo: [] }, with: Issue962::EntityWithNestedEmptyEntity)
end
end

add_swagger_documentation format: :json
end
end

subject do
get '/swagger_doc'
JSON.parse(last_response.body)
end

let(:definitions) { subject['definitions'] }
let(:entity_definition) { definitions['Issue962_EntityWithNestedEmptyEntity'] }
let(:empty_items_property) { entity_definition['properties']['empty_items'] }
let(:hidden_items_property) { entity_definition['properties']['hidden_items'] }
let(:empty_entity_definition) { definitions['Issue962_EmptyEntity'] }
let(:hidden_entity_definition) { definitions['Issue962_EntityWithHiddenProperty'] }

specify 'should generate swagger documentation without error' do
expect { subject }.not_to raise_error
end

specify do
expect(definitions.keys).to include(
'Issue962_EntityWithNestedEmptyEntity',
'Issue962_EntityWithHiddenProperty',
'Issue962_EmptyEntity'
)
end

specify do
expect(empty_items_property).to eql({
'type' => 'array',
'description' => 'This is a nested empty entity.',
'items' => {
'$ref' => '#/definitions/Issue962_EmptyEntity'
}
})
end

specify do
expect(hidden_items_property).to eql({
'type' => 'array',
'description' => 'This is a nested entity with hidden props',
'items' => {
'$ref' => '#/definitions/Issue962_EntityWithHiddenProperty'
}
})
end

specify do
expect(empty_entity_definition).to eql({
'type' => 'object',
'properties' => {}
})
end

specify do
expect(hidden_entity_definition).to eql({
'type' => 'object',
'properties' => {},
'required' => ['hidden_prop']
})
end

let(:response_schema) { subject['paths']['/issue_962']['get']['responses']['200']['schema'] }

specify do
expect(response_schema).to eql({
'$ref' => '#/definitions/Issue962_EntityWithNestedEmptyEntity'
})
end
end
37 changes: 25 additions & 12 deletions spec/support/model_parsers/entity_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,33 +135,44 @@ class DocumentedHashAndArrayModel < Grape::Entity

let(:swagger_definitions_models) do
{
'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } } },
'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } } },
'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' } } } },
'RecursiveModel' => { 'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'The name.' }, 'children' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/RecursiveModel' }, 'description' => 'The child nodes.' } } },
'DocumentedHashAndArrayModel' => { 'type' => 'object', 'properties' => { 'raw_hash' => { 'type' => 'object', 'description' => 'Example Hash.' }, 'raw_array' => { 'type' => 'array', 'description' => 'Example Array' } } }
'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } }, 'required' => %w[code message] },
'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } }, 'required' => %w[id name] },
'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' } } }, 'required' => ['description', '$responses'] },
'RecursiveModel' => { 'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'The name.' }, 'children' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/RecursiveModel' }, 'description' => 'The child nodes.' } }, 'required' => %w[name children] },
'DocumentedHashAndArrayModel' => { 'type' => 'object', 'properties' => { 'raw_hash' => { 'type' => 'object', 'description' => 'Example Hash.' }, 'raw_array' => { 'type' => 'array', 'description' => 'Example Array' } }, 'required' => %w[raw_hash raw_array] }
}
end

let(:swagger_nested_type) do
{
'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } }, 'description' => 'ApiError model' },
'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } } },
'UseItemResponseAsType' => { 'type' => 'object', 'properties' => { 'description' => { 'type' => 'string' }, 'responses' => { '$ref' => '#/definitions/ResponseItem' } }, 'description' => 'UseItemResponseAsType model' }
'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } }, 'required' => %w[code message], 'description' => 'ApiError model' },
'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } }, 'required' => %w[id name] },
'UseItemResponseAsType' => { 'type' => 'object', 'properties' => { 'description' => { 'type' => 'string' }, 'responses' => { '$ref' => '#/definitions/ResponseItem' } }, 'required' => %w[description responses], 'description' => 'UseItemResponseAsType model' }
}
end

let(:swagger_entity_as_response_object) do
{
'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } }, 'description' => 'ApiError model' },
'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } } },
'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' } } }, 'description' => 'UseResponse model' }
'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } }, 'required' => %w[code message], 'description' => 'ApiError model' },
'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'type' => 'string' } }, 'required' => %w[id name] },
'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' } } }, 'required' => ['description', '$responses'], 'description' => 'UseResponse model' }
}
end

let(:swagger_params_as_response_object) do
{
'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'ApiError model' }
'ApiError' => {
'type' => 'object',
'properties' => {
'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' },
'message' => { 'description' => 'error message', 'type' => 'string' }
},
'required' => %w[
code
message
],
'description' => 'ApiError model'
}
}
end

Expand Down Expand Up @@ -310,6 +321,7 @@ class DocumentedHashAndArrayModel < Grape::Entity
'ApiError' => {
'type' => 'object',
'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } },
'required' => %w[code message],
'description' => 'ApiError model'
},
'Something' => {
Expand All @@ -320,6 +332,7 @@ class DocumentedHashAndArrayModel < Grape::Entity
'links' => { 'type' => 'array', 'items' => { 'type' => 'link' } },
'others' => { 'type' => 'text' }
},
'required' => %w[id text links others],
'description' => 'Something model'
}
}
Expand Down
4 changes: 2 additions & 2 deletions spec/swagger_v2/errors_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
end
end

it 'should raise SwaggerSpec exception' do
expect { get '/v3/swagger_doc' }.to raise_error(GrapeSwagger::Errors::SwaggerSpec, "Empty model EmptyClass, swagger 2.0 doesn't support empty definitions.")
it 'should not raise SwaggerSpec exception' do
expect { get '/v3/swagger_doc' }.not_to raise_error(GrapeSwagger::Errors::SwaggerSpec)
end
end

Expand Down
6 changes: 5 additions & 1 deletion spec/swagger_v2/reference_entity_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ def app

expect(subject['definitions'].keys).to include 'SomethingCustom'
expect(subject['definitions']['SomethingCustom']).to eq(
'type' => 'object', 'properties' => { 'text' => { 'type' => 'string', 'description' => 'Content of something.' } }
'type' => 'object',
'properties' => { 'text' => { 'type' => 'string', 'description' => 'Content of something.' } },
'required' => ['text']
)

expect(subject['definitions'].keys).to include 'KindCustom'
Expand All @@ -103,6 +105,7 @@ def app
'description' => 'Something interesting.'
}
},
'required' => %w[title something],
'description' => 'KindCustom model'
)
end
Expand All @@ -122,6 +125,7 @@ def app
'title' => { 'type' => 'string', 'description' => 'Title of the parent.' },
'child' => { 'type' => 'string', 'description' => 'Child property.' }
},
'required' => %w[title child],
'description' => 'MyAPI::Child model'
)
end
Expand Down