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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -7
  3. data/.rubocop_todo.yml +0 -6
  4. data/.travis.yml +8 -9
  5. data/CHANGELOG.md +67 -5
  6. data/Gemfile +4 -4
  7. data/README.md +66 -3
  8. data/grape-swagger.gemspec +1 -1
  9. data/lib/grape-swagger.rb +1 -1
  10. data/lib/grape-swagger/doc_methods.rb +2 -0
  11. data/lib/grape-swagger/doc_methods/build_model_definition.rb +0 -17
  12. data/lib/grape-swagger/doc_methods/extensions.rb +6 -1
  13. data/lib/grape-swagger/doc_methods/format_data.rb +51 -0
  14. data/lib/grape-swagger/doc_methods/move_params.rb +22 -49
  15. data/lib/grape-swagger/doc_methods/parse_params.rb +6 -0
  16. data/lib/grape-swagger/endpoint.rb +32 -13
  17. data/lib/grape-swagger/endpoint/params_parser.rb +10 -17
  18. data/lib/grape-swagger/version.rb +1 -1
  19. data/spec/issues/751_deeply_nested_objects_spec.rb +190 -0
  20. data/spec/lib/endpoint/params_parser_spec.rb +44 -20
  21. data/spec/lib/endpoint_spec.rb +3 -3
  22. data/spec/lib/extensions_spec.rb +10 -0
  23. data/spec/lib/format_data_spec.rb +91 -0
  24. data/spec/lib/move_params_spec.rb +4 -266
  25. data/spec/lib/optional_object_spec.rb +0 -1
  26. data/spec/spec_helper.rb +1 -1
  27. data/spec/swagger_v2/api_swagger_v2_hash_and_array_spec.rb +3 -1
  28. data/spec/swagger_v2/api_swagger_v2_response_with_root_spec.rb +153 -0
  29. data/spec/swagger_v2/description_not_initialized_spec.rb +39 -0
  30. data/spec/swagger_v2/endpoint_versioned_path_spec.rb +33 -0
  31. data/spec/swagger_v2/mounted_target_class_spec.rb +1 -1
  32. data/spec/swagger_v2/namespace_tags_prefix_spec.rb +15 -1
  33. data/spec/swagger_v2/params_array_spec.rb +2 -2
  34. data/spec/swagger_v2/parent_less_namespace_spec.rb +32 -0
  35. data/spec/swagger_v2/{reference_entity.rb → reference_entity_spec.rb} +17 -10
  36. metadata +22 -9
  37. data/spec/swagger_v2/description_not_initialized.rb +0 -39
  38. 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 unless %w[Hash Array].include?(param_type) && !options.key?(:documentation)
28
+ memo[name] = options
42
29
  end
43
30
  end
44
31
 
45
32
  private
46
33
 
47
- def array_use_braces?(options)
48
- settings[:array_use_braces] && !(options[:documentation] && options[:documentation][:param_type] == 'body')
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrapeSwagger
4
- VERSION = '0.33.0'
4
+ VERSION = '0.34.0'
5
5
  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.first).to eq 'param_1[][param_2]'
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) { [['param_1', { type: 'Hash' }], ['param_1[param_2]', { type: 'String' }]] }
68
-
69
- it 'skips root parameter' do
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
- it 'does not add braces to the param key' do
81
- expect(parse_request_params.keys.first).to eq 'param_1[param_2]'
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
@@ -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', is_array: true }]
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', is_array: true }],
142
- ['stuffs[creators][id]', { required: true, type: 'String', is_array: true }],
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
@@ -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