diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index acb12a1e..220cb8ec 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -28,7 +28,7 @@ Metrics/AbcSize: # Offense count: 3 # Configuration parameters: CountComments. Metrics/ClassLength: - Max: 193 + Max: 195 # Offense count: 10 Metrics/CyclomaticComplexity: diff --git a/lib/grape-swagger/endpoint.rb b/lib/grape-swagger/endpoint.rb index 4ad663f8..01012787 100644 --- a/lib/grape-swagger/endpoint.rb +++ b/lib/grape-swagger/endpoint.rb @@ -229,6 +229,9 @@ def parse_request_params(required) def expose_params_from_model(model) model_name = model_name(model) + return model_name if @definitions.key?(model_name) + @definitions[model_name] = nil + properties = {} GrapeSwagger.model_parsers.each do |klass, ancestor| next unless model.ancestors.map(&:to_s).include?(ancestor) diff --git a/spec/support/model_parsers/entity_parser.rb b/spec/support/model_parsers/entity_parser.rb index 7edf07e8..e2a3e038 100644 --- a/spec/support/model_parsers/entity_parser.rb +++ b/spec/support/model_parsers/entity_parser.rb @@ -111,6 +111,11 @@ class TypedDefinition < Grape::Entity expose :prop_file, documentation: { type: File, desc: 'prop_file description' } expose :prop_json, documentation: { type: JSON, desc: 'prop_json description' } end + + class RecursiveModel < Grape::Entity + expose :name, documentation: { type: String, desc: 'The name.' } + expose :children, using: self, documentation: { type: 'RecursiveModel', is_array: true, desc: 'The child nodes.' } + end end end @@ -118,7 +123,8 @@ class TypedDefinition < Grape::Entity { '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' } } } } + '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.' } } } } end diff --git a/spec/support/model_parsers/mock_parser.rb b/spec/support/model_parsers/mock_parser.rb index e8d31ab3..27750d19 100644 --- a/spec/support/model_parsers/mock_parser.rb +++ b/spec/support/model_parsers/mock_parser.rb @@ -51,13 +51,15 @@ class QueryInputElement < OpenStruct; end class QueryInput < OpenStruct; end class ApiError < OpenStruct; end class SecondApiError < OpenStruct; end + class RecursiveModel < OpenStruct; end end end let(:swagger_definitions_models) do { 'ApiError' => { 'type' => 'object', 'properties' => {} }, - 'UseResponse' => { 'type' => 'object', 'properties' => {} } + 'UseResponse' => { 'type' => 'object', 'properties' => {} }, + 'RecursiveModel' => { 'type' => 'object', 'properties' => {} } } end diff --git a/spec/support/model_parsers/representable_parser.rb b/spec/support/model_parsers/representable_parser.rb index 1bd8cfbd..1337c2a4 100644 --- a/spec/support/model_parsers/representable_parser.rb +++ b/spec/support/model_parsers/representable_parser.rb @@ -179,6 +179,13 @@ class TypedDefinition < Representable::Decorator property :prop_file, documentation: { type: File, desc: 'prop_file description' } property :prop_json, documentation: { type: JSON, desc: 'prop_json description' } end + + class RecursiveModel < Representable::Decorator + include Representable::JSON + + property :name, documentation: { type: String, desc: 'The name.' } + property :children, decorator: self, documentation: { type: 'RecursiveModel', is_array: true, desc: 'The child nodes.' } + end end end @@ -186,7 +193,8 @@ class TypedDefinition < Representable::Decorator { 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } } }, 'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'description' => '', 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'description' => '', 'type' => 'string' } } }, - 'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'description' => '', 'type' => 'string' }, 'items' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' }, 'description' => '' } } } + 'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'description' => '', 'type' => 'string' }, 'items' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' }, 'description' => '' } } }, + 'RecursiveModel' => { 'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'The name.' }, 'children' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/RecursiveModel' }, 'description' => 'The child nodes.' } } } } end diff --git a/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb b/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb index 3de7eaab..3da049f3 100644 --- a/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb +++ b/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb @@ -10,7 +10,8 @@ class ModelApi < Grape::API add_swagger_documentation models: [ ::Entities::UseResponse, - ::Entities::ApiError + ::Entities::ApiError, + ::Entities::RecursiveModel ] end end