grape-swagger 0.30.1 → 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +1 -1
- data/CHANGELOG.md +17 -1
- data/README.md +23 -3
- data/lib/grape-swagger/doc_methods/build_model_definition.rb +16 -3
- data/lib/grape-swagger/doc_methods/move_params.rb +8 -6
- data/lib/grape-swagger/endpoint.rb +9 -39
- data/lib/grape-swagger/endpoint/params_parser.rb +73 -0
- data/lib/grape-swagger/version.rb +1 -1
- data/spec/lib/endpoint/params_parser_spec.rb +97 -0
- data/spec/lib/endpoint_spec.rb +105 -13
- data/spec/lib/move_params_spec.rb +175 -0
- data/spec/swagger_v2/params_array_spec.rb +173 -167
- data/spec/swagger_v2/params_nested_spec.rb +68 -51
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb1e3a26202efe81153ad53197d67e8e3865ba34271a841d35e921eea5dc7a77
|
4
|
+
data.tar.gz: e0d34f81243e4d327d5268d100d0f654c5fbb0b63f95e47a50ba74e7ced602b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97396d5bcecae0c152c5c67c060da11373cd080cd126f2e443766790663355ffb358743beb2b2d309fa80399b1cb813053bb83ef8f89686e0d6c63f2f2b23919
|
7
|
+
data.tar.gz: 5afd698ad351518cb9d5cc73dfe45360e182263203bab5993c2766fa5ece2cd6bf61328b90223e1b4883444798d9ae769b6f3dc32f7b442303911286d66a1e34
|
data/.rubocop_todo.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,13 +2,29 @@
|
|
2
2
|
|
3
3
|
#### Features
|
4
4
|
|
5
|
-
* [#686](https://github.com/ruby-grape/grape-swagger/pull/686): Allow response headers for responses with no content and for files - [@jdmurphy](https://github.com/jdmurphy).
|
6
5
|
* Your contribution here.
|
7
6
|
|
8
7
|
#### Fixes
|
9
8
|
|
10
9
|
* Your contribution here.
|
11
10
|
|
11
|
+
### 0.31.0 (August 22, 2018)
|
12
|
+
|
13
|
+
#### Features
|
14
|
+
|
15
|
+
* [#622](https://github.com/ruby-grape/grape-swagger/pull/622): Add support for 'brackets' collection format - [@korstiaan](https://github.com/korstiaan).
|
16
|
+
* [#637](https://github.com/ruby-grape/grape-swagger/pull/637): Add an option to add braces to array params - [@adie](https://github.com/adie).
|
17
|
+
|
18
|
+
* [#688](https://github.com/ruby-grape/grape-swagger/pull/688): Use deep merge for nested parameter definitions - [@jdmurphy](https://github.com/jdmurphy).
|
19
|
+
* [#691](https://github.com/ruby-grape/grape-swagger/pull/691): Disregard order when parsing request params for arrays - [@jdmurphy](https://github.com/jdmurphy).
|
20
|
+
* [#696](https://github.com/ruby-grape/grape-swagger/pull/696): Delegate required properties parsing to model parsers - [@Bugagazavr](https://github.com/Bugagazavr).
|
21
|
+
|
22
|
+
### 0.30.1 (July 19, 2018)
|
23
|
+
|
24
|
+
#### Features
|
25
|
+
|
26
|
+
* [#686](https://github.com/ruby-grape/grape-swagger/pull/686): Allow response headers for responses with no content and for files - [@jdmurphy](https://github.com/jdmurphy).
|
27
|
+
|
12
28
|
### 0.30.0 (July 19, 2018)
|
13
29
|
|
14
30
|
#### Features
|
data/README.md
CHANGED
@@ -50,7 +50,7 @@ grape-swagger | swagger spec | grape | grape-entity | represen
|
|
50
50
|
0.11.0 | 1.2 | >= 0.16.2 | < 0.5.0 | n/a |
|
51
51
|
0.25.2 | 2.0 | >= 0.14.0 ... <= 0.18.0 | <= 0.6.0 | >= 2.4.1 |
|
52
52
|
0.26.0 | 2.0 | >= 0.16.2 | <= 0.6.1 | >= 2.4.1 |
|
53
|
-
0.27.0 | 2.0 | >= 0.16.2 |
|
53
|
+
0.27.0 | 2.0 | >= 0.16.2 | >= 0.5.0 | >= 2.4.1 |
|
54
54
|
|
55
55
|
|
56
56
|
## Swagger-Spec <a name="swagger-spec" />
|
@@ -200,7 +200,7 @@ end
|
|
200
200
|
* [tags](#tags)
|
201
201
|
* [hide_documentation_path](#hide_documentation_path)
|
202
202
|
* [info](#info)
|
203
|
-
|
203
|
+
* [array_uses_braces](#array_uses_braces)
|
204
204
|
|
205
205
|
You can pass a hash with optional configuration settings to ```add_swagger_documentation```.
|
206
206
|
The examples show the default value.
|
@@ -372,7 +372,27 @@ add_swagger_documentation \
|
|
372
372
|
|
373
373
|
A hash merged into the `info` key of the JSON documentation.
|
374
374
|
|
375
|
-
|
375
|
+
#### array_uses_braces: <a name="array_uses_braces" />
|
376
|
+
```ruby
|
377
|
+
add_swagger_documentation \
|
378
|
+
array_use_braces: true
|
379
|
+
```
|
380
|
+
This setting must be `true` in order for params defined as an `Array` type to submit each element properly.
|
381
|
+
```ruby
|
382
|
+
params do
|
383
|
+
optional :metadata, type: Array[String]
|
384
|
+
end
|
385
|
+
```
|
386
|
+
with `array_uses_braces: true`:
|
387
|
+
```
|
388
|
+
metadata[]: { "name": "Asset ID", "value": "12345" }
|
389
|
+
metadata[]: { "name": "Asset Tag", "value": "654321"}
|
390
|
+
```
|
391
|
+
with `array_uses_braces: false`:
|
392
|
+
```
|
393
|
+
metadata: {"name": "Asset ID", "value": "123456"}
|
394
|
+
metadata: {"name": "Asset Tag", "value": "654321"}
|
395
|
+
```
|
376
396
|
|
377
397
|
## Routes Configuration <a name="routes" />
|
378
398
|
|
@@ -4,11 +4,15 @@ module GrapeSwagger
|
|
4
4
|
module DocMethods
|
5
5
|
class BuildModelDefinition
|
6
6
|
class << self
|
7
|
-
def build(model, properties)
|
7
|
+
def build(model, properties, required)
|
8
8
|
definition = { type: 'object', properties: properties }
|
9
9
|
|
10
|
-
required
|
11
|
-
|
10
|
+
if required.nil?
|
11
|
+
required_attrs = required_attributes(model)
|
12
|
+
definition[:required] = required_attrs unless required_attrs.blank?
|
13
|
+
end
|
14
|
+
|
15
|
+
definition[:required] = required if required.is_a?(Array) && required.any?
|
12
16
|
|
13
17
|
definition
|
14
18
|
end
|
@@ -22,6 +26,8 @@ module GrapeSwagger
|
|
22
26
|
def parse_entity(model)
|
23
27
|
return unless model.respond_to?(:documentation)
|
24
28
|
|
29
|
+
deprecated_workflow_for('grape-swagger-entity')
|
30
|
+
|
25
31
|
model.documentation
|
26
32
|
.select { |_name, options| options[:required] }
|
27
33
|
.map { |name, options| options[:as] || name }
|
@@ -30,10 +36,17 @@ module GrapeSwagger
|
|
30
36
|
def parse_representable(model)
|
31
37
|
return unless model.respond_to?(:map)
|
32
38
|
|
39
|
+
deprecated_workflow_for('grape-swagger-representable')
|
40
|
+
|
33
41
|
model.map
|
34
42
|
.select { |p| p[:documentation] && p[:documentation][:required] }
|
35
43
|
.map(&:name)
|
36
44
|
end
|
45
|
+
|
46
|
+
def deprecated_workflow_for(gem_name)
|
47
|
+
warn "DEPRECATED: You using old #{gem_name} version, wich not provides" \
|
48
|
+
"required attributes, to solve this problem please update #{gem_name}"
|
49
|
+
end
|
37
50
|
end
|
38
51
|
end
|
39
52
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/core_ext/hash/deep_merge'
|
4
|
+
|
3
5
|
module GrapeSwagger
|
4
6
|
module DocMethods
|
5
7
|
class MoveParams
|
@@ -114,11 +116,11 @@ module GrapeSwagger
|
|
114
116
|
|
115
117
|
def recursive_call(properties, property, nested_params)
|
116
118
|
if should_expose_as_array?(nested_params)
|
117
|
-
properties[property] = array_type
|
118
|
-
move_params_to_new(properties[property][:items], nested_params)
|
119
|
+
properties[property.to_sym] = array_type
|
120
|
+
move_params_to_new(properties[property.to_sym][:items], nested_params)
|
119
121
|
else
|
120
|
-
properties[property] = object_type
|
121
|
-
move_params_to_new(properties[property], nested_params)
|
122
|
+
properties[property.to_sym] = object_type
|
123
|
+
move_params_to_new(properties[property.to_sym], nested_params)
|
122
124
|
end
|
123
125
|
end
|
124
126
|
|
@@ -135,10 +137,10 @@ module GrapeSwagger
|
|
135
137
|
|
136
138
|
def add_properties_to_definition(definition, properties, required)
|
137
139
|
if definition.key?(:items)
|
138
|
-
definition[:items][:properties].
|
140
|
+
definition[:items][:properties].deep_merge!(properties)
|
139
141
|
add_to_required(definition[:items], required)
|
140
142
|
else
|
141
|
-
definition[:properties].
|
143
|
+
definition[:properties].deep_merge!(properties)
|
142
144
|
add_to_required(definition, required)
|
143
145
|
end
|
144
146
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_support'
|
4
|
-
require 'active_support/core_ext/string/inflections
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
require 'grape-swagger/endpoint/params_parser'
|
5
6
|
|
6
7
|
module Grape
|
7
8
|
class Endpoint
|
@@ -118,7 +119,7 @@ module Grape
|
|
118
119
|
method[:description] = description_object(route)
|
119
120
|
method[:produces] = produces_object(route, options[:produces] || options[:format])
|
120
121
|
method[:consumes] = consumes_object(route, options[:format])
|
121
|
-
method[:parameters] = params_object(route, path)
|
122
|
+
method[:parameters] = params_object(route, options, path)
|
122
123
|
method[:security] = security_object(route)
|
123
124
|
method[:responses] = response_object(route)
|
124
125
|
method[:tags] = route.options.fetch(:tags, tag_object(route, path))
|
@@ -174,8 +175,8 @@ module Grape
|
|
174
175
|
GrapeSwagger::DocMethods::ProducesConsumes.call(route.settings.dig(:description, :consumes) || format)
|
175
176
|
end
|
176
177
|
|
177
|
-
def params_object(route, path)
|
178
|
-
parameters = partition_params(route).map do |param, value|
|
178
|
+
def params_object(route, options, path)
|
179
|
+
parameters = partition_params(route, options).map do |param, value|
|
179
180
|
value = { required: false }.merge(value) if value.is_a?(Hash)
|
180
181
|
_, value = default_type([[param, value]]).first if value == ''
|
181
182
|
if value[:type]
|
@@ -279,8 +280,7 @@ module Grape
|
|
279
280
|
memo['schema'] = { type: 'file' }
|
280
281
|
end
|
281
282
|
|
282
|
-
|
283
|
-
def partition_params(route)
|
283
|
+
def partition_params(route, settings)
|
284
284
|
declared_params = route.settings[:declared_params] if route.settings[:declared_params].present?
|
285
285
|
required = merge_params(route)
|
286
286
|
required = GrapeSwagger::DocMethods::Headers.parse(route) + required unless route.headers.nil?
|
@@ -288,12 +288,11 @@ module Grape
|
|
288
288
|
default_type(required)
|
289
289
|
|
290
290
|
request_params = unless declared_params.nil? && route.headers.nil?
|
291
|
-
parse_request_params(required)
|
291
|
+
GrapeSwagger::Endpoint::ParamsParser.parse_request_params(required, settings)
|
292
292
|
end || {}
|
293
293
|
|
294
294
|
request_params.empty? ? required : request_params
|
295
295
|
end
|
296
|
-
# rubocop:enable Style/IfUnlessModifier
|
297
296
|
|
298
297
|
def merge_params(route)
|
299
298
|
param_keys = route.params.keys
|
@@ -305,27 +304,6 @@ module Grape
|
|
305
304
|
params.each { |param| param[-1] = param.last == '' ? default_param_type : param.last }
|
306
305
|
end
|
307
306
|
|
308
|
-
def parse_request_params(params)
|
309
|
-
array_key = nil
|
310
|
-
params.select { |param| public_parameter?(param) }.each_with_object({}) do |param, memo|
|
311
|
-
name, options = *param
|
312
|
-
param_type = options[:type]
|
313
|
-
param_type = param_type.to_s unless param_type.nil?
|
314
|
-
array_key = name.to_s if param_type_is_array?(param_type)
|
315
|
-
options[:is_array] = true if array_key && name.start_with?(array_key)
|
316
|
-
memo[name] = options unless %w[Hash Array].include?(param_type) && !options.key?(:documentation)
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
|
-
def param_type_is_array?(param_type)
|
321
|
-
return false unless param_type
|
322
|
-
return true if param_type == 'Array'
|
323
|
-
param_types = param_type.match(/\[(.*)\]$/)
|
324
|
-
return false unless param_types
|
325
|
-
param_types = param_types[0].split(',') if param_types
|
326
|
-
param_types.size == 1
|
327
|
-
end
|
328
|
-
|
329
307
|
def expose_params(value)
|
330
308
|
if value.is_a?(Class) && GrapeSwagger.model_parsers.find(value)
|
331
309
|
expose_params_from_model(value)
|
@@ -348,13 +326,13 @@ module Grape
|
|
348
326
|
parser = GrapeSwagger.model_parsers.find(model)
|
349
327
|
raise GrapeSwagger::Errors::UnregisteredParser, "No parser registered for #{model_name}." unless parser
|
350
328
|
|
351
|
-
properties = parser.new(model, self).call
|
329
|
+
properties, required = parser.new(model, self).call
|
352
330
|
unless properties&.any?
|
353
331
|
raise GrapeSwagger::Errors::SwaggerSpec,
|
354
332
|
"Empty model #{model_name}, swagger 2.0 doesn't support empty definitions."
|
355
333
|
end
|
356
334
|
|
357
|
-
@definitions[model_name] = GrapeSwagger::DocMethods::BuildModelDefinition.build(model, properties)
|
335
|
+
@definitions[model_name] = GrapeSwagger::DocMethods::BuildModelDefinition.build(model, properties, required)
|
358
336
|
|
359
337
|
model_name
|
360
338
|
end
|
@@ -369,13 +347,5 @@ module Grape
|
|
369
347
|
return route_hidden unless route_hidden.is_a?(Proc)
|
370
348
|
options[:token_owner] ? route_hidden.call(send(options[:token_owner].to_sym)) : route_hidden.call
|
371
349
|
end
|
372
|
-
|
373
|
-
def public_parameter?(param)
|
374
|
-
param_options = param.last
|
375
|
-
return true unless param_options.key?(:documentation) && !param_options[:required]
|
376
|
-
param_hidden = param_options[:documentation].fetch(:hidden, false)
|
377
|
-
param_hidden = param_hidden.call if param_hidden.is_a?(Proc)
|
378
|
-
!param_hidden
|
379
|
-
end
|
380
350
|
end
|
381
351
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrapeSwagger
|
4
|
+
module Endpoint
|
5
|
+
class ParamsParser
|
6
|
+
attr_reader :params, :settings
|
7
|
+
|
8
|
+
def self.parse_request_params(params, settings)
|
9
|
+
new(params, settings).parse_request_params
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(params, settings)
|
13
|
+
@params = params
|
14
|
+
@settings = settings
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_request_params
|
18
|
+
array_keys = []
|
19
|
+
public_params.each_with_object({}) do |(name, options), memo|
|
20
|
+
name = name.to_s
|
21
|
+
param_type = options[:type]
|
22
|
+
param_type = param_type.to_s unless param_type.nil?
|
23
|
+
|
24
|
+
if param_type_is_array?(param_type)
|
25
|
+
array_keys << name
|
26
|
+
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
|
39
|
+
end
|
40
|
+
|
41
|
+
memo[name] = options unless %w[Hash Array].include?(param_type) && !options.key?(:documentation)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def array_use_braces?(options)
|
48
|
+
settings[:array_use_braces] && !(options[:documentation] && options[:documentation][:param_type] == 'body')
|
49
|
+
end
|
50
|
+
|
51
|
+
def param_type_is_array?(param_type)
|
52
|
+
return false unless param_type
|
53
|
+
return true if param_type == 'Array'
|
54
|
+
param_types = param_type.match(/\[(.*)\]$/)
|
55
|
+
return false unless param_types
|
56
|
+
param_types = param_types[0].split(',') if param_types
|
57
|
+
param_types.size == 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def public_params
|
61
|
+
params.select { |param| public_parameter?(param) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def public_parameter?(param)
|
65
|
+
param_options = param.last
|
66
|
+
return true unless param_options.key?(:documentation) && !param_options[:required]
|
67
|
+
param_hidden = param_options[:documentation].fetch(:hidden, false)
|
68
|
+
param_hidden = param_hidden.call if param_hidden.is_a?(Proc)
|
69
|
+
!param_hidden
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe GrapeSwagger::Endpoint::ParamsParser do
|
6
|
+
let(:settings) { {} }
|
7
|
+
let(:params) { [] }
|
8
|
+
|
9
|
+
let(:parser) { described_class.new(params, settings) }
|
10
|
+
|
11
|
+
describe '#parse_request_params' do
|
12
|
+
context 'when param is of array type' do
|
13
|
+
let(:params) { [['param_1', { type: 'Array[String]' }]] }
|
14
|
+
|
15
|
+
it 'adds is_array option' do
|
16
|
+
expect(parser.parse_request_params).to eq('param_1' => { type: 'Array[String]', is_array: true })
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'and array_use_braces setting set to true' do
|
20
|
+
let(:settings) { { array_use_braces: true } }
|
21
|
+
|
22
|
+
it 'adds braces to the param key' do
|
23
|
+
expect(parser.parse_request_params.keys.first).to eq 'param_1[]'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when param is of simple type' do
|
29
|
+
let(:params) { [['param_1', { type: 'String' }]] }
|
30
|
+
|
31
|
+
it 'does not change options' do
|
32
|
+
expect(parser.parse_request_params).to eq('param_1' => { type: 'String' })
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'and array_use_braces setting set to true' do
|
36
|
+
let(:settings) { { array_use_braces: true } }
|
37
|
+
|
38
|
+
it 'does not add braces to the param key' do
|
39
|
+
expect(parser.parse_request_params.keys.first).to eq 'param_1'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when param is nested in a param of array type' do
|
45
|
+
let(:params) { [['param_1', { type: 'Array' }], ['param_1[param_2]', { type: 'String' }]] }
|
46
|
+
|
47
|
+
it 'skips root parameter' do
|
48
|
+
expect(parser.parse_request_params).not_to have_key 'param_1'
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'adds is_array option to the nested param' do
|
52
|
+
expect(parser.parse_request_params).to eq('param_1[param_2]' => { type: 'String', is_array: true })
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'and array_use_braces setting set to true' do
|
56
|
+
let(:settings) { { array_use_braces: true } }
|
57
|
+
|
58
|
+
it 'adds braces to the param key' do
|
59
|
+
expect(parser.parse_request_params.keys.first).to eq 'param_1[][param_2]'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when param is nested in a param of hash type' do
|
65
|
+
let(:params) { [['param_1', { type: 'Hash' }], ['param_1[param_2]', { type: 'String' }]] }
|
66
|
+
|
67
|
+
it 'skips root parameter' do
|
68
|
+
expect(parser.parse_request_params).not_to have_key 'param_1'
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'does not change options to the nested param' do
|
72
|
+
expect(parser.parse_request_params).to eq('param_1[param_2]' => { type: 'String' })
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'and array_use_braces setting set to true' do
|
76
|
+
let(:settings) { { array_use_braces: true } }
|
77
|
+
|
78
|
+
it 'does not add braces to the param key' do
|
79
|
+
expect(parser.parse_request_params.keys.first).to eq 'param_1[param_2]'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#param_type_is_array?' do
|
86
|
+
it 'returns true if the value passed represents an array' do
|
87
|
+
expect(parser.send(:param_type_is_array?, 'Array')).to be_truthy
|
88
|
+
expect(parser.send(:param_type_is_array?, '[String]')).to be_truthy
|
89
|
+
expect(parser.send(:param_type_is_array?, 'Array[Integer]')).to be_truthy
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'returns false if the value passed does not represent an array' do
|
93
|
+
expect(parser.send(:param_type_is_array?, 'String')).to be_falsey
|
94
|
+
expect(parser.send(:param_type_is_array?, '[String, Integer]')).to be_falsey
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|