grape-swagger 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -7
- data/.rubocop_todo.yml +0 -6
- data/.travis.yml +8 -9
- data/CHANGELOG.md +67 -5
- data/Gemfile +4 -4
- data/README.md +66 -3
- data/grape-swagger.gemspec +1 -1
- data/lib/grape-swagger.rb +1 -1
- data/lib/grape-swagger/doc_methods.rb +2 -0
- data/lib/grape-swagger/doc_methods/build_model_definition.rb +0 -17
- data/lib/grape-swagger/doc_methods/extensions.rb +6 -1
- data/lib/grape-swagger/doc_methods/format_data.rb +51 -0
- data/lib/grape-swagger/doc_methods/move_params.rb +22 -49
- data/lib/grape-swagger/doc_methods/parse_params.rb +6 -0
- data/lib/grape-swagger/endpoint.rb +32 -13
- data/lib/grape-swagger/endpoint/params_parser.rb +10 -17
- data/lib/grape-swagger/version.rb +1 -1
- data/spec/issues/751_deeply_nested_objects_spec.rb +190 -0
- data/spec/lib/endpoint/params_parser_spec.rb +44 -20
- data/spec/lib/endpoint_spec.rb +3 -3
- data/spec/lib/extensions_spec.rb +10 -0
- data/spec/lib/format_data_spec.rb +91 -0
- data/spec/lib/move_params_spec.rb +4 -266
- data/spec/lib/optional_object_spec.rb +0 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/swagger_v2/api_swagger_v2_hash_and_array_spec.rb +3 -1
- data/spec/swagger_v2/api_swagger_v2_response_with_root_spec.rb +153 -0
- data/spec/swagger_v2/description_not_initialized_spec.rb +39 -0
- data/spec/swagger_v2/endpoint_versioned_path_spec.rb +33 -0
- data/spec/swagger_v2/mounted_target_class_spec.rb +1 -1
- data/spec/swagger_v2/namespace_tags_prefix_spec.rb +15 -1
- data/spec/swagger_v2/params_array_spec.rb +2 -2
- data/spec/swagger_v2/parent_less_namespace_spec.rb +32 -0
- data/spec/swagger_v2/{reference_entity.rb → reference_entity_spec.rb} +17 -10
- metadata +22 -9
- data/spec/swagger_v2/description_not_initialized.rb +0 -39
- data/spec/swagger_v2/parent_less_namespace.rb +0 -49
@@ -15,37 +15,24 @@ module GrapeSwagger
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def parse_request_params
|
18
|
-
array_keys = []
|
19
18
|
public_params.each_with_object({}) do |(name, options), memo|
|
20
19
|
name = name.to_s
|
21
20
|
param_type = options[:type]
|
22
21
|
param_type = param_type.to_s unless param_type.nil?
|
23
22
|
|
24
23
|
if param_type_is_array?(param_type)
|
25
|
-
array_keys << name
|
26
24
|
options[:is_array] = true
|
27
|
-
|
28
|
-
name += '[]' if array_use_braces?(options)
|
29
|
-
else
|
30
|
-
keys = array_keys.find_all { |key| name.start_with? "#{key}[" }
|
31
|
-
if keys.any?
|
32
|
-
options[:is_array] = true
|
33
|
-
if array_use_braces?(options)
|
34
|
-
keys.sort.reverse_each do |key|
|
35
|
-
name = name.sub(key, "#{key}[]")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
25
|
+
name += '[]' if array_use_braces?
|
39
26
|
end
|
40
27
|
|
41
|
-
memo[name] = options
|
28
|
+
memo[name] = options
|
42
29
|
end
|
43
30
|
end
|
44
31
|
|
45
32
|
private
|
46
33
|
|
47
|
-
def array_use_braces?
|
48
|
-
settings[:array_use_braces] && !
|
34
|
+
def array_use_braces?
|
35
|
+
@array_use_braces ||= settings[:array_use_braces] && !includes_body_param?
|
49
36
|
end
|
50
37
|
|
51
38
|
def param_type_is_array?(param_type)
|
@@ -71,6 +58,12 @@ module GrapeSwagger
|
|
71
58
|
param_hidden = param_hidden.call if param_hidden.is_a?(Proc)
|
72
59
|
!param_hidden
|
73
60
|
end
|
61
|
+
|
62
|
+
def includes_body_param?
|
63
|
+
params.any? do |_, options|
|
64
|
+
options.dig(:documentation, :param_type) == 'body' || options.dig(:documentation, :in) == 'body'
|
65
|
+
end
|
66
|
+
end
|
74
67
|
end
|
75
68
|
end
|
76
69
|
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe '751 deeply nested objects' do
|
6
|
+
let(:app) do
|
7
|
+
Class.new(Grape::API) do
|
8
|
+
content_type :json, 'application/json; charset=UTF-8'
|
9
|
+
default_format :json
|
10
|
+
class Vrp < Grape::API
|
11
|
+
def self.vrp_request_timewindow(this)
|
12
|
+
this.optional(:start, types: [String, Float, Integer])
|
13
|
+
this.optional(:end, types: [String, Float, Integer])
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.vrp_request_point(this)
|
17
|
+
this.requires(:id, type: String, allow_blank: false)
|
18
|
+
this.optional(:matrix_index, type: Integer)
|
19
|
+
this.optional(:location, type: Hash) do
|
20
|
+
requires(:lat, type: Float, allow_blank: false)
|
21
|
+
requires(:lon, type: Float, allow_blank: false)
|
22
|
+
end
|
23
|
+
this.at_least_one_of :matrix_index, :location
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.vrp_request_activity(this)
|
27
|
+
this.optional(:duration, types: [String, Float, Integer])
|
28
|
+
this.requires(:point_id, type: String, allow_blank: false)
|
29
|
+
this.optional(:timewindows, type: Array) do
|
30
|
+
Vrp.vrp_request_timewindow(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.vrp_request_service(this)
|
35
|
+
this.requires(:id, type: String, allow_blank: false)
|
36
|
+
this.optional(:skills, type: Array[String])
|
37
|
+
|
38
|
+
this.optional(:activity, type: Hash) do
|
39
|
+
Vrp.vrp_request_activity(self)
|
40
|
+
end
|
41
|
+
this.optional(:activities, type: Array) do
|
42
|
+
Vrp.vrp_request_activity(self)
|
43
|
+
end
|
44
|
+
this.mutually_exclusive :activity, :activities
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
namespace :vrp do
|
49
|
+
resource :submit do
|
50
|
+
desc 'Submit Problems', nickname: 'vrp'
|
51
|
+
params do
|
52
|
+
optional(:vrp, type: Hash, documentation: { param_type: 'body' }) do
|
53
|
+
optional(:points, type: Array) do
|
54
|
+
Vrp.vrp_request_point(self)
|
55
|
+
end
|
56
|
+
|
57
|
+
optional(:services, type: Array) do
|
58
|
+
Vrp.vrp_request_service(self)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
post do
|
63
|
+
{ vrp: params[:vrp] }.to_json
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
add_swagger_documentation format: :json
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
subject do
|
73
|
+
get '/swagger_doc'
|
74
|
+
JSON.parse(last_response.body)
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'Correctness of vrp Points' do
|
78
|
+
let(:get_points_response) { subject['definitions']['postVrpSubmit']['properties']['vrp']['properties']['points'] }
|
79
|
+
specify do
|
80
|
+
expect(get_points_response).to eql(
|
81
|
+
'type' => 'array',
|
82
|
+
'items' => {
|
83
|
+
'type' => 'object',
|
84
|
+
'properties' => {
|
85
|
+
'id' => {
|
86
|
+
'type' => 'string'
|
87
|
+
},
|
88
|
+
'matrix_index' => {
|
89
|
+
'type' => 'integer',
|
90
|
+
'format' => 'int32'
|
91
|
+
},
|
92
|
+
'location' => {
|
93
|
+
'type' => 'object',
|
94
|
+
'properties' => {
|
95
|
+
'lat' => {
|
96
|
+
'type' => 'number',
|
97
|
+
'format' => 'float'
|
98
|
+
},
|
99
|
+
'lon' => {
|
100
|
+
'type' => 'number',
|
101
|
+
'format' => 'float'
|
102
|
+
}
|
103
|
+
},
|
104
|
+
'required' => %w[lat lon]
|
105
|
+
}
|
106
|
+
},
|
107
|
+
'required' => ['id']
|
108
|
+
}
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'Correctness of vrp Services' do
|
114
|
+
let(:get_service_response) { subject['definitions']['postVrpSubmit']['properties']['vrp']['properties']['services'] }
|
115
|
+
specify do
|
116
|
+
expect(get_service_response).to include(
|
117
|
+
'type' => 'array',
|
118
|
+
'items' => {
|
119
|
+
'type' => 'object',
|
120
|
+
'properties' => {
|
121
|
+
'id' => {
|
122
|
+
'type' => 'string'
|
123
|
+
},
|
124
|
+
'skills' => {
|
125
|
+
'type' => 'array',
|
126
|
+
'items' => {
|
127
|
+
'type' => 'string'
|
128
|
+
}
|
129
|
+
},
|
130
|
+
'activity' => {
|
131
|
+
'type' => 'object',
|
132
|
+
'properties' => {
|
133
|
+
'duration' => {
|
134
|
+
'type' => 'string'
|
135
|
+
},
|
136
|
+
'point_id' => {
|
137
|
+
'type' => 'string'
|
138
|
+
},
|
139
|
+
'timewindows' => {
|
140
|
+
'type' => 'array',
|
141
|
+
'items' => {
|
142
|
+
'type' => 'object',
|
143
|
+
'properties' => {
|
144
|
+
'start' => {
|
145
|
+
'type' => 'string'
|
146
|
+
},
|
147
|
+
'end' => {
|
148
|
+
'type' => 'string'
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
}
|
153
|
+
},
|
154
|
+
'required' => ['point_id']
|
155
|
+
}, 'activities' => {
|
156
|
+
'type' => 'array',
|
157
|
+
'items' => {
|
158
|
+
'type' => 'object',
|
159
|
+
'properties' => {
|
160
|
+
'duration' => {
|
161
|
+
'type' => 'string'
|
162
|
+
},
|
163
|
+
'point_id' => {
|
164
|
+
'type' => 'string'
|
165
|
+
},
|
166
|
+
'timewindows' => {
|
167
|
+
'type' => 'array',
|
168
|
+
'items' => {
|
169
|
+
'type' => 'object',
|
170
|
+
'properties' => {
|
171
|
+
'start' => {
|
172
|
+
'type' => 'string'
|
173
|
+
},
|
174
|
+
'end' => {
|
175
|
+
'type' => 'string'
|
176
|
+
}
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
},
|
181
|
+
'required' => ['point_id']
|
182
|
+
}
|
183
|
+
}
|
184
|
+
},
|
185
|
+
'required' => ['id']
|
186
|
+
}
|
187
|
+
)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -46,39 +46,63 @@ describe GrapeSwagger::Endpoint::ParamsParser do
|
|
46
46
|
context 'when param is nested in a param of array type' do
|
47
47
|
let(:params) { [['param_1', { type: 'Array' }], ['param_1[param_2]', { type: 'String' }]] }
|
48
48
|
|
49
|
-
it 'skips root parameter' do
|
50
|
-
is_expected.not_to have_key 'param_1'
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'adds is_array option to the nested param' do
|
54
|
-
expect(parse_request_params['param_1[param_2]']).to eq(type: 'String', is_array: true)
|
55
|
-
end
|
56
|
-
|
57
49
|
context 'and array_use_braces setting set to true' do
|
58
50
|
let(:settings) { { array_use_braces: true } }
|
59
51
|
|
60
52
|
it 'adds braces to the param key' do
|
61
|
-
expect(parse_request_params.keys.
|
53
|
+
expect(parse_request_params.keys.last).to eq 'param_1[param_2]'
|
62
54
|
end
|
63
55
|
end
|
64
56
|
end
|
65
57
|
|
66
58
|
context 'when param is nested in a param of hash type' do
|
67
|
-
let(:params) { [
|
68
|
-
|
69
|
-
|
70
|
-
is_expected.not_to have_key 'param_1'
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'does not change options to the nested param' do
|
74
|
-
expect(parse_request_params['param_1[param_2]']).to eq(type: 'String')
|
75
|
-
end
|
59
|
+
let(:params) { [param_1, param_2] }
|
60
|
+
let(:param_1) { ['param_1', { type: 'Hash' }] }
|
61
|
+
let(:param_2) { ['param_1[param_2]', { type: 'String' }] }
|
76
62
|
|
77
63
|
context 'and array_use_braces setting set to true' do
|
78
64
|
let(:settings) { { array_use_braces: true } }
|
79
65
|
|
80
|
-
|
81
|
-
|
66
|
+
context 'and param is of simple type' do
|
67
|
+
it 'does not add braces to the param key' do
|
68
|
+
expect(parse_request_params.keys.last).to eq 'param_1[param_2]'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'and param is of array type' do
|
73
|
+
let(:param_2) { ['param_1[param_2]', { type: 'Array[String]' }] }
|
74
|
+
|
75
|
+
it 'adds braces to the param key' do
|
76
|
+
expect(parse_request_params.keys.last).to eq 'param_1[param_2][]'
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'and `param_type` option is set to body' do
|
80
|
+
let(:param_2) do
|
81
|
+
['param_1[param_2]', { type: 'Array[String]', documentation: { param_type: 'body' } }]
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'does not add braces to the param key' do
|
85
|
+
expect(parse_request_params.keys.last).to eq 'param_1[param_2]'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'and `in` option is set to body' do
|
90
|
+
let(:param_2) do
|
91
|
+
['param_1[param_2]', { type: 'Array[String]', documentation: { in: 'body' } }]
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'does not add braces to the param key' do
|
95
|
+
expect(parse_request_params.keys.last).to eq 'param_1[param_2]'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'and hash `param_type` option is set to body' do
|
100
|
+
let(:param_1) { ['param_1', { type: 'Hash', documentation: { param_type: 'body' } }] }
|
101
|
+
|
102
|
+
it 'does not add braces to the param key' do
|
103
|
+
expect(parse_request_params.keys.last).to eq 'param_1[param_2]'
|
104
|
+
end
|
105
|
+
end
|
82
106
|
end
|
83
107
|
end
|
84
108
|
end
|
data/spec/lib/endpoint_spec.rb
CHANGED
@@ -109,7 +109,7 @@ describe Grape::Endpoint do
|
|
109
109
|
['id', { required: true, type: 'String' }],
|
110
110
|
['description', { required: false, type: 'String' }],
|
111
111
|
['stuffs', { required: true, type: 'Array', is_array: true }],
|
112
|
-
['stuffs[id]', { required: true, type: 'String'
|
112
|
+
['stuffs[id]', { required: true, type: 'String' }]
|
113
113
|
]
|
114
114
|
end
|
115
115
|
|
@@ -138,8 +138,8 @@ describe Grape::Endpoint do
|
|
138
138
|
['stuffs', { required: true, type: 'Array', is_array: true }],
|
139
139
|
['stuffs[owners]', { required: true, type: 'Array', is_array: true }],
|
140
140
|
['stuffs[creators]', { required: true, type: 'Array', is_array: true }],
|
141
|
-
['stuffs[owners][id]', { required: true, type: 'String'
|
142
|
-
['stuffs[creators][id]', { required: true, type: 'String'
|
141
|
+
['stuffs[owners][id]', { required: true, type: 'String' }],
|
142
|
+
['stuffs[creators][id]', { required: true, type: 'String' }],
|
143
143
|
['stuffs_and_things', { required: true, type: 'String' }]
|
144
144
|
]
|
145
145
|
end
|
data/spec/lib/extensions_spec.rb
CHANGED
@@ -3,6 +3,16 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe GrapeSwagger::DocMethods::Extensions do
|
6
|
+
context 'it should not break method introspection' do
|
7
|
+
describe '.method' do
|
8
|
+
describe 'method introspection' do
|
9
|
+
specify do
|
10
|
+
expect(described_class.method(described_class.methods.first)).to be_a(Method)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
6
16
|
describe '#find_definition' do
|
7
17
|
subject { described_class }
|
8
18
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe GrapeSwagger::DocMethods::FormatData do
|
6
|
+
let(:subject) { GrapeSwagger::DocMethods::FormatData }
|
7
|
+
|
8
|
+
[true, false].each do |array_use_braces|
|
9
|
+
context 'when param is nested in a param of array type' do
|
10
|
+
let(:braces) { array_use_braces ? '[]' : '' }
|
11
|
+
let(:params) do
|
12
|
+
[
|
13
|
+
{ in: 'formData', name: "param1#{braces}", type: 'array', items: { type: 'string' } },
|
14
|
+
{ in: 'formData', name: 'param1[param2]', type: 'string' }
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'skips root parameter' do
|
19
|
+
expect(subject.to_format(params).first).not_to have_key "param1#{braces}"
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'Move array type to param2' do
|
23
|
+
expect(subject.to_format(params).first).to include(name: "param1#{braces}[param2]", type: 'array')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when param is nested in a param of hash type' do
|
29
|
+
let(:params) { [{ in: 'formData', type: 'object', name: 'param1' }, { in: 'formData', name: 'param1[param2]', type: 'string' }] }
|
30
|
+
|
31
|
+
it 'skips root parameter' do
|
32
|
+
expect(subject.to_format(params).first).not_to have_key 'param1'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'Move array type to param2' do
|
36
|
+
expect(subject.to_format(params).first).to include(name: 'param1[param2]', type: 'string')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when params contain a complex array' do
|
41
|
+
let(:params) do
|
42
|
+
[
|
43
|
+
{ name: 'id', required: true, type: 'string' },
|
44
|
+
{ name: 'description', required: false, type: 'string' },
|
45
|
+
{ name: 'stuffs', required: true, type: 'array' },
|
46
|
+
{ name: 'stuffs[id]', required: true, type: 'string' }
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:expected_params) do
|
51
|
+
[
|
52
|
+
{ name: 'id', required: true, type: 'string' },
|
53
|
+
{ name: 'description', required: false, type: 'string' },
|
54
|
+
{ name: 'stuffs[id]', required: true, type: 'array', items: { type: 'string' } }
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'parses params correctly and adds array type all concerned elements' do
|
59
|
+
expect(subject.to_format(params)).to eq expected_params
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when array params are not contiguous with parent array' do
|
63
|
+
let(:params) do
|
64
|
+
[
|
65
|
+
{ name: 'id', required: true, type: 'string' },
|
66
|
+
{ name: 'description', required: false, type: 'string' },
|
67
|
+
{ name: 'stuffs', required: true, type: 'array' },
|
68
|
+
{ name: 'stuffs[owners]', required: true, type: 'array' },
|
69
|
+
{ name: 'stuffs[creators]', required: true, type: 'array' },
|
70
|
+
{ name: 'stuffs[owners][id]', required: true, type: 'string' },
|
71
|
+
{ name: 'stuffs[creators][id]', required: true, type: 'string' },
|
72
|
+
{ name: 'stuffs_and_things', required: true, type: 'string' }
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
let(:expected_params) do
|
77
|
+
[
|
78
|
+
{ name: 'id', required: true, type: 'string' },
|
79
|
+
{ name: 'description', required: false, type: 'string' },
|
80
|
+
{ name: 'stuffs[owners][id]', required: true, type: 'array', items: { type: 'string' } },
|
81
|
+
{ name: 'stuffs[creators][id]', required: true, type: 'array', items: { type: 'string' } },
|
82
|
+
{ name: 'stuffs_and_things', required: true, type: 'string' }
|
83
|
+
]
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'parses params correctly and adds array type all concerned elements' do
|
87
|
+
expect(subject.to_format(params)).to eq expected_params
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|