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 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