Skip to content

Commit

Permalink
Support recursive models
Browse files Browse the repository at this point in the history
The model can reference itself in some cases, e.g., when representing a
tree structure.
  • Loading branch information
lest committed May 10, 2016
1 parent 7602537 commit ce7999e
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Metrics/AbcSize:
# Offense count: 3
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 193
Max: 195

# Offense count: 10
Metrics/CyclomaticComplexity:
Expand Down
3 changes: 3 additions & 0 deletions lib/grape-swagger/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion spec/support/model_parsers/entity_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,20 @@ 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

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' } } } }
'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

Expand Down
4 changes: 3 additions & 1 deletion spec/support/model_parsers/mock_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 9 additions & 1 deletion spec/support/model_parsers/representable_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,22 @@ 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

let(:swagger_definitions_models) do
{
'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

Expand Down
3 changes: 2 additions & 1 deletion spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class ModelApi < Grape::API

add_swagger_documentation models: [
::Entities::UseResponse,
::Entities::ApiError
::Entities::ApiError,
::Entities::RecursiveModel
]
end
end
Expand Down

0 comments on commit ce7999e

Please sign in to comment.