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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2cc4d92b47209a6323502fd863a738a8829e0d78b599a47e83a1aa9e52746cf7
4
- data.tar.gz: cbc408aaf0f686032312bdfb3332671d73341e90eeff694162f2d0bdcfc14a81
3
+ metadata.gz: fb1e3a26202efe81153ad53197d67e8e3865ba34271a841d35e921eea5dc7a77
4
+ data.tar.gz: e0d34f81243e4d327d5268d100d0f654c5fbb0b63f95e47a50ba74e7ced602b2
5
5
  SHA512:
6
- metadata.gz: 980563a6d50454d8f7f4439b96a820423a77758ee0c20a8ea353439ddcf46a596cf2763cdd714b07530d63c71b539c50e18775aeba2639168ae0755920887dd8
7
- data.tar.gz: af45e27dccdbe492ba950a6dbf29f9e3f0e4348aea8e689813cc5ed32fdd595250c2dbb689a7aa41890dcdaa33280321fb704b0e2e72975e6356631d030ae370
6
+ metadata.gz: 97396d5bcecae0c152c5c67c060da11373cd080cd126f2e443766790663355ffb358743beb2b2d309fa80399b1cb813053bb83ef8f89686e0d6c63f2f2b23919
7
+ data.tar.gz: 5afd698ad351518cb9d5cc73dfe45360e182263203bab5993c2766fa5ece2cd6bf61328b90223e1b4883444798d9ae769b6f3dc32f7b442303911286d66a1e34
@@ -21,7 +21,7 @@ Metrics/AbcSize:
21
21
  # Offense count: 3
22
22
  # Configuration parameters: CountComments.
23
23
  Metrics/ClassLength:
24
- Max: 287
24
+ Max: 248
25
25
 
26
26
  # Offense count: 10
27
27
  Metrics/CyclomaticComplexity:
@@ -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 | => 0.5.0 | >= 2.4.1 |
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 = required_attributes(model)
11
- definition[:required] = required unless required.blank?
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].merge!(properties)
140
+ definition[:items][:properties].deep_merge!(properties)
139
141
  add_to_required(definition[:items], required)
140
142
  else
141
- definition[:properties].merge!(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.rb'
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
- # rubocop:disable Style/IfUnlessModifier
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrapeSwagger
4
- VERSION = '0.30.1'
4
+ VERSION = '0.31.0'
5
5
  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