Skip to content

Commit ac90d9f

Browse files
blakenumbata
authored andcommitted
begin to parse params
1 parent f29e14c commit ac90d9f

File tree

4 files changed

+275
-8
lines changed

4 files changed

+275
-8
lines changed

lib/grape-swagger/openapi_3/doc_methods.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# frozen_string_literal: true
22

3-
# require 'grape-swagger/openapi_3/endpoint'
4-
53
require 'grape-swagger/doc_methods/status_codes'
64
require 'grape-swagger/doc_methods/produces_consumes'
75
require 'grape-swagger/doc_methods/data_type'
@@ -10,7 +8,7 @@
108
require 'grape-swagger/doc_methods/optional_object'
119
require 'grape-swagger/doc_methods/path_string'
1210
require 'grape-swagger/doc_methods/tag_name_description'
13-
require 'grape-swagger/doc_methods/parse_params'
11+
require 'grape-swagger/openapi_3/doc_methods/parse_params'
1412
require 'grape-swagger/doc_methods/move_params'
1513
require 'grape-swagger/doc_methods/headers'
1614
require 'grape-swagger/doc_methods/build_model_definition'
@@ -52,7 +50,7 @@ def setup(options)
5250
options
5351
)
5452

55-
paths, definitions = endpoint.path_and_definition_objects(combi_routes, options)
53+
paths, definitions = endpoint.path_and_definition_objects(combi_routes, target_class, options)
5654
tags = tags_from(paths, options)
5755

5856
output[:tags] = tags unless tags.empty? || paths.blank?
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# frozen_string_literal: true
2+
3+
module GrapeSwagger
4+
module DocMethods
5+
class ParseParams
6+
class << self
7+
def call(param, settings, path, route, definitions)
8+
method = route.request_method
9+
additional_documentation = settings.fetch(:documentation, {})
10+
settings.merge!(additional_documentation)
11+
data_type = DataType.call(settings)
12+
13+
value_type = settings.merge(data_type: data_type, path: path, param_name: param, method: method)
14+
15+
# required properties
16+
@parsed_param = {
17+
in: param_type(value_type),
18+
name: settings[:full_name] || param
19+
}
20+
21+
# optional properties
22+
document_description(settings)
23+
document_type_and_format(settings, data_type)
24+
document_array_param(value_type, definitions) if value_type[:is_array]
25+
document_default_value(settings) unless value_type[:is_array]
26+
document_range_values(settings) unless value_type[:is_array]
27+
document_required(settings)
28+
29+
@parsed_param
30+
end
31+
32+
private
33+
34+
def document_description(settings)
35+
description = settings[:desc] || settings[:description]
36+
@parsed_param[:description] = description if description
37+
end
38+
39+
def document_required(settings)
40+
@parsed_param[:required] = settings[:required] || false
41+
@parsed_param[:required] = true if @parsed_param[:in] == 'path'
42+
end
43+
44+
def document_range_values(settings)
45+
values = settings[:values] || nil
46+
enum_or_range_values = parse_enum_or_range_values(values)
47+
@parsed_param.merge!(enum_or_range_values) if enum_or_range_values
48+
end
49+
50+
def document_default_value(settings)
51+
@parsed_param[:default] = settings[:default] if settings[:default].present?
52+
end
53+
54+
def document_type_and_format(settings, data_type)
55+
if DataType.primitive?(data_type)
56+
data = DataType.mapping(data_type)
57+
@parsed_param[:type], @parsed_param[:format] = data
58+
else
59+
@parsed_param[:type] = data_type
60+
end
61+
@parsed_param[:format] = settings[:format] if settings[:format].present?
62+
end
63+
64+
def document_array_param(value_type, definitions)
65+
if value_type[:documentation].present?
66+
param_type = value_type[:documentation][:param_type]
67+
doc_type = value_type[:documentation][:type]
68+
type = DataType.mapping(doc_type) if doc_type && !DataType.request_primitive?(doc_type)
69+
collection_format = value_type[:documentation][:collectionFormat]
70+
end
71+
72+
param_type ||= value_type[:param_type]
73+
74+
array_items = {}
75+
if definitions[value_type[:data_type]]
76+
array_items['$ref'] = "#/definitions/#{@parsed_param[:type]}"
77+
else
78+
array_items[:type] = type || @parsed_param[:type] == 'array' ? 'string' : @parsed_param[:type]
79+
end
80+
array_items[:format] = @parsed_param.delete(:format) if @parsed_param[:format]
81+
82+
values = value_type[:values] || nil
83+
enum_or_range_values = parse_enum_or_range_values(values)
84+
array_items.merge!(enum_or_range_values) if enum_or_range_values
85+
86+
array_items[:default] = value_type[:default] if value_type[:default].present?
87+
88+
@parsed_param[:in] = param_type || 'formData'
89+
@parsed_param[:items] = array_items
90+
@parsed_param[:type] = 'array'
91+
@parsed_param[:collectionFormat] = collection_format if DataType.collections.include?(collection_format)
92+
end
93+
94+
def param_type(value_type)
95+
param_type = value_type[:param_type] || value_type[:in]
96+
if value_type[:path].include?("{#{value_type[:param_name]}}")
97+
'path'
98+
elsif param_type
99+
param_type
100+
elsif %w[POST PUT PATCH].include?(value_type[:method])
101+
DataType.request_primitive?(value_type[:data_type]) ? 'formData' : 'body'
102+
else
103+
'query'
104+
end
105+
end
106+
107+
def parse_enum_or_range_values(values)
108+
case values
109+
when Proc
110+
parse_enum_or_range_values(values.call) if values.parameters.empty?
111+
when Range
112+
parse_range_values(values) if values.first.is_a?(Integer)
113+
else
114+
{ enum: values } if values
115+
end
116+
end
117+
118+
def parse_range_values(values)
119+
{ minimum: values.first, maximum: values.last }
120+
end
121+
end
122+
end
123+
end
124+
end

lib/grape-swagger/openapi_3/endpoint.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ def contact_object(infos)
7272
end
7373

7474
# building path and definitions objects
75-
def path_and_definition_objects(namespace_routes, options)
75+
def path_and_definition_objects(namespace_routes, target_class, options)
76+
@content_types = content_types_for(target_class)
77+
7678
@paths = {}
7779
@definitions = {}
7880
namespace_routes.each_key do |key|
@@ -114,8 +116,8 @@ def method_object(route, options, path)
114116
method = {}
115117
method[:summary] = summary_object(route)
116118
method[:description] = description_object(route)
117-
method[:produces] = produces_object(route, options[:produces] || options[:format])
118-
method[:consumes] = consumes_object(route, options[:format])
119+
# method[:produces] = produces_object(route, options[:produces] || options[:format])
120+
# method[:consumes] = consumes_object(route, options[:format])
119121
method[:parameters] = params_object(route, options, path)
120122
method[:security] = security_object(route)
121123
method[:responses] = response_object(route)
@@ -216,8 +218,9 @@ def response_object(route)
216218
next unless !response_model.start_with?('Swagger_doc') && (@definitions[response_model] || value[:model])
217219

218220
@definitions[response_model][:description] = description_object(route)
221+
ref = build_reference(route, value, response_model)
219222

220-
memo[value[:code]][:schema] = build_reference(route, value, response_model)
223+
memo[value[:code]][:content] = @content_types.map { |c| [c, { schema: ref }] }.to_h
221224
memo[value[:code]][:examples] = value[:examples] if value[:examples]
222225
end
223226
end
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe 'response' do
6+
include_context "#{MODEL_PARSER} swagger example"
7+
8+
before :all do
9+
module TheApi
10+
class ResponseApi < Grape::API
11+
format :json
12+
13+
desc 'This returns something',
14+
params: Entities::UseResponse.documentation,
15+
failure: [{ code: 400, message: 'NotFound', model: Entities::ApiError }]
16+
post '/params_given' do
17+
{ 'declared_params' => declared(params) }
18+
end
19+
20+
desc 'This returns something',
21+
entity: Entities::UseResponse,
22+
failure: [{ code: 400, message: 'NotFound', model: Entities::ApiError }]
23+
get '/entity_response' do
24+
{ 'declared_params' => declared(params) }
25+
end
26+
27+
desc 'This returns something',
28+
entity: Entities::UseItemResponseAsType,
29+
failure: [{ code: 400, message: 'NotFound', model: Entities::ApiError }]
30+
get '/nested_type' do
31+
{ 'declared_params' => declared(params) }
32+
end
33+
34+
add_swagger_documentation openapi_version: '3.0'
35+
end
36+
end
37+
end
38+
39+
def app
40+
TheApi::ResponseApi
41+
end
42+
43+
describe 'uses nested type as response object' do
44+
subject do
45+
get '/swagger_doc/nested_type'
46+
JSON.parse(last_response.body)
47+
end
48+
specify do
49+
expect(subject['paths']['/nested_type']['get']).to eql(
50+
'description' => 'This returns something',
51+
'responses' => {
52+
'200' => {
53+
'description' => 'This returns something',
54+
'content' => {
55+
'application/json' => {
56+
'schema' => { '$ref' => '#/definitions/UseItemResponseAsType' }
57+
}
58+
}
59+
},
60+
'400' => {
61+
'description' => 'NotFound',
62+
'content' => {
63+
'application/json' => {
64+
'schema' => { '$ref' => '#/definitions/ApiError' }
65+
}
66+
}
67+
}
68+
},
69+
'tags' => ['nested_type'],
70+
'operationId' => 'getNestedType'
71+
)
72+
expect(subject['definitions']).to eql(swagger_nested_type)
73+
end
74+
end
75+
76+
describe 'uses entity as response object' do
77+
subject do
78+
get '/swagger_doc/entity_response'
79+
JSON.parse(last_response.body)
80+
end
81+
82+
specify do
83+
expect(subject['paths']['/entity_response']['get']).to eql(
84+
'description' => 'This returns something',
85+
'responses' => {
86+
'200' => {
87+
'description' => 'This returns something',
88+
'content' => {
89+
'application/json' => {
90+
'schema' => { '$ref' => '#/definitions/UseResponse' }
91+
}
92+
}
93+
},
94+
'400' => {
95+
'description' => 'NotFound',
96+
'content' => {
97+
'application/json' => {
98+
'schema' => { '$ref' => '#/definitions/ApiError' }
99+
}
100+
}
101+
}
102+
},
103+
'tags' => ['entity_response'],
104+
'operationId' => 'getEntityResponse'
105+
)
106+
expect(subject['definitions']).to eql(swagger_entity_as_response_object)
107+
end
108+
end
109+
110+
describe 'uses params as response object' do
111+
subject do
112+
get '/swagger_doc/params_given'
113+
JSON.parse(last_response.body)
114+
end
115+
116+
specify do
117+
expect(subject['paths']['/params_given']['post']).to eql(
118+
'description' => 'This returns something',
119+
'parameters' => [
120+
{ 'in' => 'formData', 'name' => 'description', 'type' => 'string', 'required' => false },
121+
{ 'in' => 'formData', 'name' => '$responses', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => false }
122+
],
123+
'responses' => {
124+
'201' => {
125+
'description' => 'This returns something'
126+
},
127+
'400' => {
128+
'description' => 'NotFound',
129+
'content' => {
130+
'application/json' => {
131+
'schema' => { '$ref' => '#/definitions/ApiError' }
132+
}
133+
}
134+
}
135+
},
136+
'tags' => ['params_given'],
137+
'operationId' => 'postParamsGiven'
138+
)
139+
expect(subject['definitions']).to eql(swagger_params_as_response_object)
140+
end
141+
end
142+
end

0 commit comments

Comments
 (0)