grape-swagger 0.30.1 → 0.31.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_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
|