grape 1.7.1 → 2.0.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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -1
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +30 -25
  5. data/UPGRADING.md +35 -0
  6. data/grape.gemspec +3 -6
  7. data/lib/grape/api.rb +2 -2
  8. data/lib/grape/content_types.rb +2 -8
  9. data/lib/grape/dsl/desc.rb +1 -1
  10. data/lib/grape/dsl/inside_route.rb +11 -11
  11. data/lib/grape/dsl/request_response.rb +2 -1
  12. data/lib/grape/dsl/settings.rb +2 -6
  13. data/lib/grape/endpoint.rb +28 -18
  14. data/lib/grape/error_formatter/base.rb +1 -1
  15. data/lib/grape/exceptions/base.rb +2 -2
  16. data/lib/grape/exceptions/missing_group_type.rb +1 -6
  17. data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
  18. data/lib/grape/exceptions/validation_errors.rb +1 -6
  19. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
  20. data/lib/grape/extensions/hash.rb +4 -7
  21. data/lib/grape/extensions/hashie/mash.rb +3 -3
  22. data/lib/grape/formatter/serializable_hash.rb +7 -7
  23. data/lib/grape/http/headers.rb +12 -2
  24. data/lib/grape/middleware/auth/base.rb +1 -1
  25. data/lib/grape/middleware/auth/strategies.rb +1 -2
  26. data/lib/grape/middleware/error.rb +5 -5
  27. data/lib/grape/middleware/formatter.rb +6 -6
  28. data/lib/grape/middleware/versioner/header.rb +11 -19
  29. data/lib/grape/railtie.rb +9 -0
  30. data/lib/grape/request.rb +8 -2
  31. data/lib/grape/router/route.rb +1 -3
  32. data/lib/grape/util/lazy_value.rb +3 -11
  33. data/lib/grape/util/strict_hash_configuration.rb +3 -4
  34. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  35. data/lib/grape/validations/params_scope.rb +8 -2
  36. data/lib/grape/validations/single_attribute_iterator.rb +3 -1
  37. data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
  38. data/lib/grape/validations/validators/base.rb +9 -20
  39. data/lib/grape/validations/validators/default_validator.rb +2 -20
  40. data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
  41. data/lib/grape/validations/validators/values_validator.rb +14 -5
  42. data/lib/grape/version.rb +1 -1
  43. data/lib/grape.rb +26 -5
  44. metadata +13 -253
  45. data/lib/grape/config.rb +0 -34
  46. data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
  47. data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
  48. data/spec/grape/api/custom_validations_spec.rb +0 -256
  49. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  50. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  51. data/spec/grape/api/documentation_spec.rb +0 -59
  52. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  53. data/spec/grape/api/instance_spec.rb +0 -103
  54. data/spec/grape/api/invalid_format_spec.rb +0 -45
  55. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  56. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  57. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  58. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  59. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  60. data/spec/grape/api/recognize_path_spec.rb +0 -21
  61. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  62. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  63. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  64. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  65. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  66. data/spec/grape/api_remount_spec.rb +0 -473
  67. data/spec/grape/api_spec.rb +0 -4347
  68. data/spec/grape/config_spec.rb +0 -17
  69. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  70. data/spec/grape/dsl/desc_spec.rb +0 -101
  71. data/spec/grape/dsl/headers_spec.rb +0 -62
  72. data/spec/grape/dsl/helpers_spec.rb +0 -100
  73. data/spec/grape/dsl/inside_route_spec.rb +0 -535
  74. data/spec/grape/dsl/logger_spec.rb +0 -24
  75. data/spec/grape/dsl/middleware_spec.rb +0 -60
  76. data/spec/grape/dsl/parameters_spec.rb +0 -180
  77. data/spec/grape/dsl/request_response_spec.rb +0 -206
  78. data/spec/grape/dsl/routing_spec.rb +0 -275
  79. data/spec/grape/dsl/settings_spec.rb +0 -261
  80. data/spec/grape/dsl/validations_spec.rb +0 -55
  81. data/spec/grape/endpoint/declared_spec.rb +0 -846
  82. data/spec/grape/endpoint_spec.rb +0 -1085
  83. data/spec/grape/entity_spec.rb +0 -336
  84. data/spec/grape/exceptions/base_spec.rb +0 -81
  85. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -145
  86. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  87. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  88. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  89. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  90. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -21
  91. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  92. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  93. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  94. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  95. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -23
  96. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  97. data/spec/grape/exceptions/validation_spec.rb +0 -19
  98. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  99. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  100. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  101. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  102. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  103. data/spec/grape/integration/rack_spec.rb +0 -51
  104. data/spec/grape/loading_spec.rb +0 -44
  105. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  106. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  107. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  108. data/spec/grape/middleware/base_spec.rb +0 -221
  109. data/spec/grape/middleware/error_spec.rb +0 -85
  110. data/spec/grape/middleware/exception_spec.rb +0 -294
  111. data/spec/grape/middleware/formatter_spec.rb +0 -461
  112. data/spec/grape/middleware/globals_spec.rb +0 -30
  113. data/spec/grape/middleware/stack_spec.rb +0 -155
  114. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  115. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  116. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  117. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  118. data/spec/grape/middleware/versioner_spec.rb +0 -21
  119. data/spec/grape/named_api_spec.rb +0 -19
  120. data/spec/grape/parser_spec.rb +0 -86
  121. data/spec/grape/path_spec.rb +0 -252
  122. data/spec/grape/presenters/presenter_spec.rb +0 -71
  123. data/spec/grape/request_spec.rb +0 -136
  124. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  125. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  126. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  127. data/spec/grape/util/stackable_values_spec.rb +0 -128
  128. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  129. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  130. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  131. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -40
  132. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  133. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -57
  134. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  135. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  136. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  137. data/spec/grape/validations/types_spec.rb +0 -111
  138. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  139. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  140. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  141. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  142. data/spec/grape/validations/validators/default_spec.rb +0 -463
  143. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  144. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  145. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  146. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  147. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  148. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  149. data/spec/grape/validations/validators/values_spec.rb +0 -696
  150. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  151. data/spec/grape/validations_spec.rb +0 -2029
  152. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  153. data/spec/integration/multi_json/json_spec.rb +0 -7
  154. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  155. data/spec/shared/versioning_examples.rb +0 -215
  156. data/spec/spec_helper.rb +0 -52
  157. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  158. data/spec/support/chunks.rb +0 -14
  159. data/spec/support/content_type_helpers.rb +0 -15
  160. data/spec/support/endpoint_faker.rb +0 -25
  161. data/spec/support/file_streamer.rb +0 -13
  162. data/spec/support/integer_helpers.rb +0 -13
  163. data/spec/support/versioned_helpers.rb +0 -55
@@ -1,2029 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations do
4
- subject { Class.new(Grape::API) }
5
-
6
- def app
7
- subject
8
- end
9
-
10
- def declared_params
11
- subject.namespace_stackable(:declared_params).flatten
12
- end
13
-
14
- describe 'params' do
15
- context 'optional' do
16
- before do
17
- subject.params do
18
- optional :a_number, regexp: /^[0-9]+$/
19
- optional :attachment, type: File
20
- end
21
- subject.get '/optional' do
22
- 'optional works!'
23
- end
24
- end
25
-
26
- it 'validates when params is present' do
27
- get '/optional', a_number: 'string'
28
- expect(last_response.status).to eq(400)
29
- expect(last_response.body).to eq('a_number is invalid')
30
-
31
- get '/optional', a_number: 45
32
- expect(last_response.status).to eq(200)
33
- expect(last_response.body).to eq('optional works!')
34
- end
35
-
36
- it "doesn't validate when param not present" do
37
- get '/optional', a_number: nil, attachment: nil
38
- expect(last_response.status).to eq(200)
39
- expect(last_response.body).to eq('optional works!')
40
- end
41
-
42
- it 'adds to declared parameters' do
43
- subject.params do
44
- optional :some_param
45
- end
46
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([:some_param])
47
- end
48
- end
49
-
50
- context 'optional using Grape::Entity documentation' do
51
- def define_optional_using
52
- documentation = { field_a: { type: String }, field_b: { type: String } }
53
- subject.params do
54
- optional :all, using: documentation
55
- end
56
- end
57
- before do
58
- define_optional_using
59
- subject.get '/optional' do
60
- 'optional with using works'
61
- end
62
- end
63
-
64
- it 'adds entity documentation to declared params' do
65
- define_optional_using
66
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq(%i[field_a field_b])
67
- end
68
-
69
- it 'works when field_a and field_b are not present' do
70
- get '/optional'
71
- expect(last_response.status).to eq(200)
72
- expect(last_response.body).to eq('optional with using works')
73
- end
74
-
75
- it 'works when field_a is present' do
76
- get '/optional', field_a: 'woof'
77
- expect(last_response.status).to eq(200)
78
- expect(last_response.body).to eq('optional with using works')
79
- end
80
-
81
- it 'works when field_b is present' do
82
- get '/optional', field_b: 'woof'
83
- expect(last_response.status).to eq(200)
84
- expect(last_response.body).to eq('optional with using works')
85
- end
86
- end
87
-
88
- context 'required' do
89
- before do
90
- subject.params do
91
- requires :key, type: String
92
- end
93
- subject.get('/required') { 'required works' }
94
- subject.put('/required') { { key: params[:key] }.to_json }
95
- end
96
-
97
- it 'errors when param not present' do
98
- get '/required'
99
- expect(last_response.status).to eq(400)
100
- expect(last_response.body).to eq('key is missing')
101
- end
102
-
103
- it "doesn't throw a missing param when param is present" do
104
- get '/required', key: 'cool'
105
- expect(last_response.status).to eq(200)
106
- expect(last_response.body).to eq('required works')
107
- end
108
-
109
- it 'adds to declared parameters' do
110
- subject.params do
111
- requires :some_param
112
- end
113
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([:some_param])
114
- end
115
-
116
- it 'works when required field is present but nil' do
117
- put '/required', { key: nil }.to_json, 'CONTENT_TYPE' => 'application/json'
118
- expect(last_response.status).to eq(200)
119
- expect(JSON.parse(last_response.body)).to eq('key' => nil)
120
- end
121
- end
122
-
123
- context 'requires with nested params' do
124
- before do
125
- subject.params do
126
- requires :first_level, type: Hash do
127
- optional :second_level, type: Array do
128
- requires :value, type: Integer
129
- optional :name, type: String
130
- optional :third_level, type: Array do
131
- requires :value, type: Integer
132
- optional :name, type: String
133
- optional :fourth_level, type: Array do
134
- requires :value, type: Integer
135
- optional :name, type: String
136
- end
137
- end
138
- end
139
- end
140
- end
141
- subject.put('/required') { 'required works' }
142
- end
143
-
144
- let(:request_params) do
145
- {
146
- first_level: {
147
- second_level: [
148
- { value: 1, name: 'Lisa' },
149
- {
150
- value: 2,
151
- name: 'James',
152
- third_level: [
153
- { value: 'three', name: 'Sophie' },
154
- {
155
- value: 4,
156
- name: 'Jenny',
157
- fourth_level: [
158
- { name: 'Samuel' }, { value: 6, name: 'Jane' }
159
- ]
160
- }
161
- ]
162
- }
163
- ]
164
- }
165
- }
166
- end
167
-
168
- it 'validates correctly in deep nested params' do
169
- put '/required', request_params.to_json, 'CONTENT_TYPE' => 'application/json'
170
-
171
- expect(last_response.status).to eq(400)
172
- expect(last_response.body).to eq(
173
- 'first_level[second_level][1][third_level][0][value] is invalid, ' \
174
- 'first_level[second_level][1][third_level][1][fourth_level][0][value] is missing'
175
- )
176
- end
177
- end
178
-
179
- context 'requires :all using Grape::Entity documentation' do
180
- def define_requires_all
181
- documentation = {
182
- required_field: { type: String },
183
- optional_field: { type: String }
184
- }
185
- subject.params do
186
- requires :all, except: :optional_field, using: documentation
187
- end
188
- end
189
- before do
190
- define_requires_all
191
- subject.get '/required' do
192
- 'required works'
193
- end
194
- end
195
-
196
- it 'adds entity documentation to declared params' do
197
- define_requires_all
198
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq(%i[required_field optional_field])
199
- end
200
-
201
- it 'errors when required_field is not present' do
202
- get '/required'
203
- expect(last_response.status).to eq(400)
204
- expect(last_response.body).to eq('required_field is missing')
205
- end
206
-
207
- it 'works when required_field is present' do
208
- get '/required', required_field: 'woof'
209
- expect(last_response.status).to eq(200)
210
- expect(last_response.body).to eq('required works')
211
- end
212
- end
213
-
214
- context 'requires :none using Grape::Entity documentation' do
215
- def define_requires_none
216
- documentation = {
217
- required_field: { type: String },
218
- optional_field: { type: String }
219
- }
220
- subject.params do
221
- requires :none, except: :required_field, using: documentation
222
- end
223
- end
224
- before do
225
- define_requires_none
226
- subject.get '/required' do
227
- 'required works'
228
- end
229
- end
230
-
231
- it 'adds entity documentation to declared params' do
232
- define_requires_none
233
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq(%i[required_field optional_field])
234
- end
235
-
236
- it 'errors when required_field is not present' do
237
- get '/required'
238
- expect(last_response.status).to eq(400)
239
- expect(last_response.body).to eq('required_field is missing')
240
- end
241
-
242
- it 'works when required_field is present' do
243
- get '/required', required_field: 'woof'
244
- expect(last_response.status).to eq(200)
245
- expect(last_response.body).to eq('required works')
246
- end
247
- end
248
-
249
- context 'requires :all or :none but except a non-existent field using Grape::Entity documentation' do
250
- context 'requires :all' do
251
- def define_requires_all
252
- documentation = {
253
- required_field: { type: String },
254
- optional_field: { type: String }
255
- }
256
- subject.params do
257
- requires :all, except: :non_existent_field, using: documentation
258
- end
259
- end
260
-
261
- it 'adds only the entity documentation to declared params, nothing more' do
262
- define_requires_all
263
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq(%i[required_field optional_field])
264
- end
265
- end
266
-
267
- context 'requires :none' do
268
- def define_requires_none
269
- documentation = {
270
- required_field: { type: String },
271
- optional_field: { type: String }
272
- }
273
- subject.params do
274
- requires :none, except: :non_existent_field, using: documentation
275
- end
276
- end
277
-
278
- it 'adds only the entity documentation to declared params, nothing more' do
279
- expect { define_requires_none }.to raise_error(ArgumentError)
280
- end
281
- end
282
- end
283
-
284
- context 'required with an Array block' do
285
- before do
286
- subject.params do
287
- requires :items, type: Array do
288
- requires :key
289
- end
290
- end
291
- subject.get('/required') { 'required works' }
292
- subject.put('/required') { { items: params[:items] }.to_json }
293
- end
294
-
295
- it 'errors when param not present' do
296
- get '/required'
297
- expect(last_response.status).to eq(400)
298
- expect(last_response.body).to eq('items is missing')
299
- end
300
-
301
- it 'errors when param is not an Array' do
302
- get '/required', items: 'hello'
303
- expect(last_response.status).to eq(400)
304
- expect(last_response.body).to eq('items is invalid')
305
-
306
- get '/required', items: { key: 'foo' }
307
- expect(last_response.status).to eq(400)
308
- expect(last_response.body).to eq('items is invalid')
309
- end
310
-
311
- it "doesn't throw a missing param when param is present" do
312
- get '/required', items: [{ key: 'hello' }, { key: 'world' }]
313
- expect(last_response.status).to eq(200)
314
- expect(last_response.body).to eq('required works')
315
- end
316
-
317
- it "doesn't throw a missing param when param is present but empty" do
318
- put '/required', { items: [] }.to_json, 'CONTENT_TYPE' => 'application/json'
319
- expect(last_response.status).to eq(200)
320
- expect(JSON.parse(last_response.body)).to eq('items' => [])
321
- end
322
-
323
- it 'adds to declared parameters' do
324
- subject.params do
325
- requires :items, type: Array do
326
- requires :key
327
- end
328
- end
329
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key]])
330
- end
331
- end
332
-
333
- # Ensure there is no leakage between declared Array types and
334
- # subsequent Hash types
335
- context 'required with an Array and a Hash block' do
336
- before do
337
- subject.params do
338
- requires :cats, type: Array[String], default: []
339
- requires :items, type: Hash do
340
- requires :key
341
- end
342
- end
343
- subject.get '/required' do
344
- 'required works'
345
- end
346
- end
347
-
348
- it 'does not output index [0] for Hash types' do
349
- get '/required', cats: ['Garfield'], items: { foo: 'bar' }
350
- expect(last_response.status).to eq(400)
351
- expect(last_response.body).to eq('items[key] is missing')
352
- end
353
- end
354
-
355
- context 'required with a Hash block' do
356
- before do
357
- subject.params do
358
- requires :items, type: Hash do
359
- requires :key
360
- end
361
- end
362
- subject.get '/required' do
363
- 'required works'
364
- end
365
- end
366
-
367
- it 'errors when param not present' do
368
- get '/required'
369
- expect(last_response.status).to eq(400)
370
- expect(last_response.body).to eq('items is missing, items[key] is missing')
371
- end
372
-
373
- it 'errors when nested param not present' do
374
- get '/required', items: { foo: 'bar' }
375
- expect(last_response.status).to eq(400)
376
- expect(last_response.body).to eq('items[key] is missing')
377
- end
378
-
379
- it 'errors when param is not a Hash' do
380
- get '/required', items: 'hello'
381
- expect(last_response.status).to eq(400)
382
- expect(last_response.body).to eq('items is invalid, items[key] is missing')
383
-
384
- get '/required', items: [{ key: 'foo' }]
385
- expect(last_response.status).to eq(400)
386
- expect(last_response.body).to eq('items is invalid')
387
- end
388
-
389
- it "doesn't throw a missing param when param is present" do
390
- get '/required', items: { key: 'hello' }
391
- expect(last_response.status).to eq(200)
392
- expect(last_response.body).to eq('required works')
393
- end
394
-
395
- it 'adds to declared parameters' do
396
- subject.params do
397
- requires :items, type: Array do
398
- requires :key
399
- end
400
- end
401
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key]])
402
- end
403
- end
404
-
405
- context 'hash with a required param with validation' do
406
- before do
407
- subject.params do
408
- requires :items, type: Hash do
409
- requires :key, type: String, values: %w[a b]
410
- end
411
- end
412
- subject.get '/required' do
413
- 'required works'
414
- end
415
- end
416
-
417
- it 'errors when param is not a Hash' do
418
- get '/required', items: 'not a hash'
419
- expect(last_response.status).to eq(400)
420
- expect(last_response.body).to eq('items is invalid, items[key] is missing, items[key] is invalid')
421
-
422
- get '/required', items: [{ key: 'hash in array' }]
423
- expect(last_response.status).to eq(400)
424
- expect(last_response.body).to eq('items is invalid, items[key] does not have a valid value')
425
- end
426
-
427
- it 'works when all params match' do
428
- get '/required', items: { key: 'a' }
429
- expect(last_response.status).to eq(200)
430
- expect(last_response.body).to eq('required works')
431
- end
432
- end
433
-
434
- context 'group' do
435
- before do
436
- subject.params do
437
- group :items, type: Array do
438
- requires :key
439
- end
440
- end
441
- subject.get '/required' do
442
- 'required works'
443
- end
444
- end
445
-
446
- it 'errors when param not present' do
447
- get '/required'
448
- expect(last_response.status).to eq(400)
449
- expect(last_response.body).to eq('items is missing')
450
- end
451
-
452
- it "doesn't throw a missing param when param is present" do
453
- get '/required', items: [key: 'hello']
454
- expect(last_response.status).to eq(200)
455
- expect(last_response.body).to eq('required works')
456
- end
457
-
458
- it 'adds to declared parameters' do
459
- subject.params do
460
- group :items, type: Array do
461
- requires :key
462
- end
463
- end
464
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key]])
465
- end
466
- end
467
-
468
- context 'group params with nested params which has a type' do
469
- let(:invalid_items) { { items: '' } }
470
-
471
- before do
472
- subject.params do
473
- optional :items, type: Array do
474
- optional :key1, type: String
475
- optional :key2, type: String
476
- end
477
- end
478
- subject.post '/group_with_nested' do
479
- 'group with nested works'
480
- end
481
- end
482
-
483
- it 'errors when group param is invalid' do
484
- post '/group_with_nested', items: invalid_items
485
- expect(last_response.status).to eq(400)
486
- end
487
- end
488
-
489
- context 'custom validator for a Hash' do
490
- let(:date_range_validator) do
491
- Class.new(Grape::Validations::Validators::Base) do
492
- def validate_param!(attr_name, params)
493
- return if params[attr_name][:from] <= params[attr_name][:to]
494
-
495
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'")
496
- end
497
- end
498
- end
499
-
500
- before do
501
- described_class.register_validator('date_range', date_range_validator)
502
- end
503
-
504
- after do
505
- described_class.deregister_validator('date_range')
506
- end
507
-
508
- before do
509
- subject.params do
510
- optional :date_range, date_range: true, type: Hash do
511
- requires :from, type: Integer
512
- requires :to, type: Integer
513
- end
514
- end
515
- subject.get('/optional') do
516
- 'optional works'
517
- end
518
- subject.params do
519
- requires :date_range, date_range: true, type: Hash do
520
- requires :from, type: Integer
521
- requires :to, type: Integer
522
- end
523
- end
524
- subject.get('/required') do
525
- 'required works'
526
- end
527
- end
528
-
529
- context 'which is optional' do
530
- it "doesn't throw an error if the validation passes" do
531
- get '/optional', date_range: { from: 1, to: 2 }
532
- expect(last_response.status).to eq(200)
533
- end
534
-
535
- it 'errors if the validation fails' do
536
- get '/optional', date_range: { from: 2, to: 1 }
537
- expect(last_response.status).to eq(400)
538
- end
539
- end
540
-
541
- context 'which is required' do
542
- it "doesn't throw an error if the validation passes" do
543
- get '/required', date_range: { from: 1, to: 2 }
544
- expect(last_response.status).to eq(200)
545
- end
546
-
547
- it 'errors if the validation fails' do
548
- get '/required', date_range: { from: 2, to: 1 }
549
- expect(last_response.status).to eq(400)
550
- end
551
- end
552
- end
553
-
554
- context 'validation within arrays' do
555
- before do
556
- subject.params do
557
- group :children, type: Array do
558
- requires :name
559
- group :parents, type: Array do
560
- requires :name, allow_blank: false
561
- end
562
- end
563
- end
564
- subject.get '/within_array' do
565
- 'within array works'
566
- end
567
- end
568
-
569
- it 'can handle new scopes within child elements' do
570
- get '/within_array', children: [
571
- { name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
572
- { name: 'Joe', parents: [{ name: 'Josie' }] }
573
- ]
574
- expect(last_response.status).to eq(200)
575
- expect(last_response.body).to eq('within array works')
576
- end
577
-
578
- it 'errors when a parameter is not present' do
579
- get '/within_array', children: [
580
- { name: 'Jim', parents: [{ name: 'Joy' }] },
581
- { name: 'Job', parents: [{}] }
582
- ]
583
- # NOTE: with body parameters in json or XML or similar this
584
- # should actually fail with: children[parents][name] is missing.
585
- expect(last_response.status).to eq(400)
586
- expect(last_response.body).to eq('children[1][parents] is missing, children[0][parents][1][name] is missing, children[0][parents][1][name] is empty')
587
- end
588
-
589
- it 'errors when a parameter is not present in array within array' do
590
- get '/within_array', children: [
591
- { name: 'Jim', parents: [{ name: 'Joy' }] },
592
- { name: 'Job', parents: [{ name: 'Bill' }, { name: '' }] }
593
- ]
594
-
595
- expect(last_response.status).to eq(400)
596
- expect(last_response.body).to eq('children[1][parents][1][name] is empty')
597
- end
598
-
599
- it 'handle errors for all array elements' do
600
- get '/within_array', children: [
601
- { name: 'Jim', parents: [] },
602
- { name: 'Job', parents: [] }
603
- ]
604
-
605
- expect(last_response.status).to eq(400)
606
- expect(last_response.body).to eq(
607
- 'children[0][parents][0][name] is missing, ' \
608
- 'children[1][parents][0][name] is missing'
609
- )
610
- end
611
-
612
- it 'safely handles empty arrays and blank parameters' do
613
- # NOTE: with body parameters in json or XML or similar this
614
- # should actually return 200, since an empty array is valid.
615
- get '/within_array', children: []
616
- expect(last_response.status).to eq(400)
617
- expect(last_response.body).to eq(
618
- 'children[0][name] is missing, ' \
619
- 'children[0][parents] is missing, ' \
620
- 'children[0][parents] is invalid, ' \
621
- 'children[0][parents][0][name] is missing, ' \
622
- 'children[0][parents][0][name] is empty'
623
- )
624
-
625
- get '/within_array', children: [name: 'Jay']
626
- expect(last_response.status).to eq(400)
627
- expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing, children[0][parents][0][name] is empty')
628
- end
629
-
630
- it 'errors when param is not an Array' do
631
- get '/within_array', children: 'hello'
632
- expect(last_response.status).to eq(400)
633
- expect(last_response.body).to eq('children is invalid')
634
-
635
- get '/within_array', children: { name: 'foo' }
636
- expect(last_response.status).to eq(400)
637
- expect(last_response.body).to eq('children is invalid')
638
-
639
- get '/within_array', children: [name: 'Jay', parents: { name: 'Fred' }]
640
- expect(last_response.status).to eq(400)
641
- expect(last_response.body).to eq('children[0][parents] is invalid')
642
- end
643
- end
644
-
645
- context 'with block param' do
646
- before do
647
- subject.params do
648
- requires :planets, type: Array do
649
- requires :name
650
- end
651
- end
652
- subject.get '/req' do
653
- 'within array works'
654
- end
655
- subject.put '/req' do
656
- ''
657
- end
658
-
659
- subject.params do
660
- group :stars, type: Array do
661
- requires :name
662
- end
663
- end
664
- subject.get '/grp' do
665
- 'within array works'
666
- end
667
- subject.put '/grp' do
668
- ''
669
- end
670
-
671
- subject.params do
672
- requires :name
673
- optional :moons, type: Array do
674
- requires :name
675
- end
676
- end
677
- subject.get '/opt' do
678
- 'within array works'
679
- end
680
- subject.put '/opt' do
681
- ''
682
- end
683
- end
684
-
685
- it 'requires defaults to Array type' do
686
- get '/req', planets: 'Jupiter, Saturn'
687
- expect(last_response.status).to eq(400)
688
- expect(last_response.body).to eq('planets is invalid')
689
-
690
- get '/req', planets: { name: 'Jupiter' }
691
- expect(last_response.status).to eq(400)
692
- expect(last_response.body).to eq('planets is invalid')
693
-
694
- get '/req', planets: [{ name: 'Venus' }, { name: 'Mars' }]
695
- expect(last_response.status).to eq(200)
696
-
697
- put_with_json '/req', planets: []
698
- expect(last_response.status).to eq(200)
699
- end
700
-
701
- it 'optional defaults to Array type' do
702
- get '/opt', name: 'Jupiter', moons: 'Europa, Ganymede'
703
- expect(last_response.status).to eq(400)
704
- expect(last_response.body).to eq('moons is invalid')
705
-
706
- get '/opt', name: 'Jupiter', moons: { name: 'Ganymede' }
707
- expect(last_response.status).to eq(400)
708
- expect(last_response.body).to eq('moons is invalid')
709
-
710
- get '/opt', name: 'Jupiter', moons: [{ name: 'Io' }, { name: 'Callisto' }]
711
- expect(last_response.status).to eq(200)
712
-
713
- put_with_json '/opt', name: 'Venus'
714
- expect(last_response.status).to eq(200)
715
-
716
- put_with_json '/opt', name: 'Mercury', moons: []
717
- expect(last_response.status).to eq(200)
718
- end
719
-
720
- it 'group defaults to Array type' do
721
- get '/grp', stars: 'Sun'
722
- expect(last_response.status).to eq(400)
723
- expect(last_response.body).to eq('stars is invalid')
724
-
725
- get '/grp', stars: { name: 'Sun' }
726
- expect(last_response.status).to eq(400)
727
- expect(last_response.body).to eq('stars is invalid')
728
-
729
- get '/grp', stars: [{ name: 'Sun' }]
730
- expect(last_response.status).to eq(200)
731
-
732
- put_with_json '/grp', stars: []
733
- expect(last_response.status).to eq(200)
734
- end
735
- end
736
-
737
- context 'validation within arrays with JSON' do
738
- before do
739
- subject.params do
740
- group :children, type: Array do
741
- requires :name
742
- group :parents, type: Array do
743
- requires :name
744
- end
745
- end
746
- end
747
- subject.put '/within_array' do
748
- 'within array works'
749
- end
750
- end
751
-
752
- it 'can handle new scopes within child elements' do
753
- put_with_json '/within_array', children: [
754
- { name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
755
- { name: 'Joe', parents: [{ name: 'Josie' }] }
756
- ]
757
- expect(last_response.status).to eq(200)
758
- expect(last_response.body).to eq('within array works')
759
- end
760
-
761
- it 'errors when a parameter is not present' do
762
- put_with_json '/within_array', children: [
763
- { name: 'Jim', parents: [{}] },
764
- { name: 'Job', parents: [{ name: 'Joy' }] }
765
- ]
766
- expect(last_response.status).to eq(400)
767
- expect(last_response.body).to eq('children[0][parents][0][name] is missing')
768
- end
769
-
770
- it 'safely handles empty arrays and blank parameters' do
771
- put_with_json '/within_array', children: []
772
- expect(last_response.status).to eq(200)
773
- put_with_json '/within_array', children: [name: 'Jay']
774
- expect(last_response.status).to eq(400)
775
- expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing')
776
- end
777
- end
778
-
779
- context 'optional with an Array block' do
780
- before do
781
- subject.params do
782
- optional :items, type: Array do
783
- requires :key
784
- end
785
- end
786
- subject.get '/optional_group' do
787
- 'optional group works'
788
- end
789
- end
790
-
791
- it "doesn't throw a missing param when the group isn't present" do
792
- get '/optional_group'
793
- expect(last_response.status).to eq(200)
794
- expect(last_response.body).to eq('optional group works')
795
- end
796
-
797
- it "doesn't throw a missing param when both group and param are given" do
798
- get '/optional_group', items: [{ key: 'foo' }]
799
- expect(last_response.status).to eq(200)
800
- expect(last_response.body).to eq('optional group works')
801
- end
802
-
803
- it 'errors when group is present, but required param is not' do
804
- get '/optional_group', items: [{ not_key: 'foo' }]
805
- expect(last_response.status).to eq(400)
806
- expect(last_response.body).to eq('items[0][key] is missing')
807
- end
808
-
809
- it "errors when param is present but isn't an Array" do
810
- get '/optional_group', items: 'hello'
811
- expect(last_response.status).to eq(400)
812
- expect(last_response.body).to eq('items is invalid')
813
-
814
- get '/optional_group', items: { key: 'foo' }
815
- expect(last_response.status).to eq(400)
816
- expect(last_response.body).to eq('items is invalid')
817
- end
818
-
819
- it 'adds to declared parameters' do
820
- subject.params do
821
- optional :items, type: Array do
822
- requires :key
823
- end
824
- end
825
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key]])
826
- end
827
- end
828
-
829
- context 'nested optional Array blocks' do
830
- before do
831
- subject.params do
832
- optional :items, type: Array do
833
- requires :key
834
- optional(:optional_subitems, type: Array) { requires :value }
835
- requires(:required_subitems, type: Array) { requires :value }
836
- end
837
- end
838
- subject.get('/nested_optional_group') { 'nested optional group works' }
839
- end
840
-
841
- it 'does no internal validations if the outer group is blank' do
842
- get '/nested_optional_group'
843
- expect(last_response.status).to eq(200)
844
- expect(last_response.body).to eq('nested optional group works')
845
- end
846
-
847
- it 'does internal validations if the outer group is present' do
848
- get '/nested_optional_group', items: [{ key: 'foo' }]
849
- expect(last_response.status).to eq(400)
850
- expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
851
-
852
- get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
853
- expect(last_response.status).to eq(200)
854
- expect(last_response.body).to eq('nested optional group works')
855
- end
856
-
857
- it 'handles deep nesting' do
858
- get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
859
- expect(last_response.status).to eq(400)
860
- expect(last_response.body).to eq('items[0][optional_subitems][0][value] is missing')
861
-
862
- get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ value: 'baz' }] }]
863
- expect(last_response.status).to eq(200)
864
- expect(last_response.body).to eq('nested optional group works')
865
- end
866
-
867
- it 'handles validation within arrays' do
868
- get '/nested_optional_group', items: [{ key: 'foo' }]
869
- expect(last_response.status).to eq(400)
870
- expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
871
-
872
- get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
873
- expect(last_response.status).to eq(200)
874
- expect(last_response.body).to eq('nested optional group works')
875
-
876
- get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
877
- expect(last_response.status).to eq(400)
878
- expect(last_response.body).to eq('items[0][optional_subitems][0][value] is missing')
879
- end
880
-
881
- it 'adds to declared parameters' do
882
- subject.params do
883
- optional :items, type: Array do
884
- requires :key
885
- optional(:optional_subitems, type: Array) { requires :value }
886
- requires(:required_subitems, type: Array) { requires :value }
887
- end
888
- end
889
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
890
- end
891
-
892
- context <<~DESC do
893
- Issue occurs whenever:
894
- * param structure with at least three levels
895
- * 1st level item is a required Array that has >1 entry with an optional item present and >1 entry with an optional item missing#{' '}
896
- * 2nd level is an optional Array or Hash#{' '}
897
- * 3rd level is a required item (can be any type)
898
- * additional levels do not effect the issue from occuring
899
- DESC
900
-
901
- it 'example based off actual real world use case' do
902
- subject.params do
903
- requires :orders, type: Array do
904
- requires :id, type: Integer
905
- optional :drugs, type: Array do
906
- requires :batches, type: Array do
907
- requires :batch_no, type: String
908
- end
909
- end
910
- end
911
- end
912
-
913
- subject.get '/validate_required_arrays_under_optional_arrays' do
914
- 'validate_required_arrays_under_optional_arrays works!'
915
- end
916
-
917
- data = {
918
- orders: [
919
- { id: 77, drugs: [{ batches: [{ batch_no: 'A1234567' }] }] },
920
- { id: 70 }
921
- ]
922
- }
923
-
924
- get '/validate_required_arrays_under_optional_arrays', data
925
- expect(last_response.body).to eq('validate_required_arrays_under_optional_arrays works!')
926
- expect(last_response.status).to eq(200)
927
- end
928
-
929
- it 'simplest example using Array -> Array -> Hash -> String' do
930
- subject.params do
931
- requires :orders, type: Array do
932
- requires :id, type: Integer
933
- optional :drugs, type: Array do
934
- requires :batch_no, type: String
935
- end
936
- end
937
- end
938
-
939
- subject.get '/validate_required_arrays_under_optional_arrays' do
940
- 'validate_required_arrays_under_optional_arrays works!'
941
- end
942
-
943
- data = {
944
- orders: [
945
- { id: 77, drugs: [{ batch_no: 'A1234567' }] },
946
- { id: 70 }
947
- ]
948
- }
949
-
950
- get '/validate_required_arrays_under_optional_arrays', data
951
- expect(last_response.body).to eq('validate_required_arrays_under_optional_arrays works!')
952
- expect(last_response.status).to eq(200)
953
- end
954
-
955
- it 'simplest example using Array -> Hash -> String' do
956
- subject.params do
957
- requires :orders, type: Array do
958
- requires :id, type: Integer
959
- optional :drugs, type: Hash do
960
- requires :batch_no, type: String
961
- end
962
- end
963
- end
964
-
965
- subject.get '/validate_required_arrays_under_optional_arrays' do
966
- 'validate_required_arrays_under_optional_arrays works!'
967
- end
968
-
969
- data = {
970
- orders: [
971
- { id: 77, drugs: { batch_no: 'A1234567' } },
972
- { id: 70 }
973
- ]
974
- }
975
-
976
- get '/validate_required_arrays_under_optional_arrays', data
977
- expect(last_response.body).to eq('validate_required_arrays_under_optional_arrays works!')
978
- expect(last_response.status).to eq(200)
979
- end
980
-
981
- it 'correctly indexes invalida data' do
982
- subject.params do
983
- requires :orders, type: Array do
984
- requires :id, type: Integer
985
- optional :drugs, type: Array do
986
- requires :batch_no, type: String
987
- requires :quantity, type: Integer
988
- end
989
- end
990
- end
991
-
992
- subject.get '/correctly_indexes' do
993
- 'correctly_indexes works!'
994
- end
995
-
996
- data = {
997
- orders: [
998
- { id: 70 },
999
- { id: 77, drugs: [{ batch_no: 'A1234567', quantity: 12 }, { batch_no: 'B222222' }] }
1000
- ]
1001
- }
1002
-
1003
- get '/correctly_indexes', data
1004
- expect(last_response.body).to eq('orders[1][drugs][1][quantity] is missing')
1005
- expect(last_response.status).to eq(400)
1006
- end
1007
-
1008
- context 'multiple levels of optional and requires settings' do
1009
- before do
1010
- subject.params do
1011
- requires :top, type: Array do
1012
- requires :top_id, type: Integer, allow_blank: false
1013
- optional :middle_1, type: Array do
1014
- requires :middle_1_id, type: Integer, allow_blank: false
1015
- optional :middle_2, type: Array do
1016
- requires :middle_2_id, type: String, allow_blank: false
1017
- optional :bottom, type: Array do
1018
- requires :bottom_id, type: Integer, allow_blank: false
1019
- end
1020
- end
1021
- end
1022
- end
1023
- end
1024
-
1025
- subject.get '/multi_level' do
1026
- 'multi_level works!'
1027
- end
1028
- end
1029
-
1030
- it 'with valid data' do
1031
- data = {
1032
- top: [
1033
- { top_id: 1, middle_1: [
1034
- { middle_1_id: 11 }, { middle_1_id: 12, middle_2: [
1035
- { middle_2_id: 121 }, { middle_2_id: 122, bottom: [{ bottom_id: 1221 }] }
1036
- ] }
1037
- ] },
1038
- { top_id: 2, middle_1: [
1039
- { middle_1_id: 21 }, { middle_1_id: 22, middle_2: [
1040
- { middle_2_id: 221 }
1041
- ] }
1042
- ] },
1043
- { top_id: 3, middle_1: [
1044
- { middle_1_id: 31 }, { middle_1_id: 32 }
1045
- ] },
1046
- { top_id: 4 }
1047
- ]
1048
- }
1049
-
1050
- get '/multi_level', data
1051
- expect(last_response.body).to eq('multi_level works!')
1052
- expect(last_response.status).to eq(200)
1053
- end
1054
-
1055
- it 'with invalid data' do
1056
- data = {
1057
- top: [
1058
- { top_id: 1, middle_1: [
1059
- { middle_1_id: 11 }, { middle_1_id: 12, middle_2: [
1060
- { middle_2_id: 121 }, { middle_2_id: 122, bottom: [{ bottom_id: nil }] }
1061
- ] }
1062
- ] },
1063
- { top_id: 2, middle_1: [
1064
- { middle_1_id: 21 }, { middle_1_id: 22, middle_2: [{ middle_2_id: nil }] }
1065
- ] },
1066
- { top_id: 3, middle_1: [
1067
- { middle_1_id: nil }, { middle_1_id: 32 }
1068
- ] },
1069
- { top_id: nil, missing_top_id: 4 }
1070
- ]
1071
- }
1072
- # debugger
1073
- get '/multi_level', data
1074
- expect(last_response.body.split(', ')).to contain_exactly(
1075
- 'top[3][top_id] is empty',
1076
- 'top[2][middle_1][0][middle_1_id] is empty',
1077
- 'top[1][middle_1][1][middle_2][0][middle_2_id] is empty',
1078
- 'top[0][middle_1][1][middle_2][1][bottom][0][bottom_id] is empty'
1079
- )
1080
- expect(last_response.status).to eq(400)
1081
- end
1082
- end
1083
- end
1084
-
1085
- it 'exactly_one_of' do
1086
- subject.params do
1087
- requires :orders, type: Array do
1088
- requires :id, type: Integer
1089
- optional :drugs, type: Hash do
1090
- optional :batch_no, type: String
1091
- optional :batch_id, type: String
1092
- exactly_one_of :batch_no, :batch_id
1093
- end
1094
- end
1095
- end
1096
-
1097
- subject.get '/exactly_one_of' do
1098
- 'exactly_one_of works!'
1099
- end
1100
-
1101
- data = {
1102
- orders: [
1103
- { id: 77, drugs: { batch_no: 'A1234567' } },
1104
- { id: 70 }
1105
- ]
1106
- }
1107
-
1108
- get '/exactly_one_of', data
1109
- expect(last_response.body).to eq('exactly_one_of works!')
1110
- expect(last_response.status).to eq(200)
1111
- end
1112
-
1113
- it 'at_least_one_of' do
1114
- subject.params do
1115
- requires :orders, type: Array do
1116
- requires :id, type: Integer
1117
- optional :drugs, type: Hash do
1118
- optional :batch_no, type: String
1119
- optional :batch_id, type: String
1120
- at_least_one_of :batch_no, :batch_id
1121
- end
1122
- end
1123
- end
1124
-
1125
- subject.get '/at_least_one_of' do
1126
- 'at_least_one_of works!'
1127
- end
1128
-
1129
- data = {
1130
- orders: [
1131
- { id: 77, drugs: { batch_no: 'A1234567' } },
1132
- { id: 70 }
1133
- ]
1134
- }
1135
-
1136
- get '/at_least_one_of', data
1137
- expect(last_response.body).to eq('at_least_one_of works!')
1138
- expect(last_response.status).to eq(200)
1139
- end
1140
-
1141
- it 'all_or_none_of' do
1142
- subject.params do
1143
- requires :orders, type: Array do
1144
- requires :id, type: Integer
1145
- optional :drugs, type: Hash do
1146
- optional :batch_no, type: String
1147
- optional :batch_id, type: String
1148
- all_or_none_of :batch_no, :batch_id
1149
- end
1150
- end
1151
- end
1152
-
1153
- subject.get '/all_or_none_of' do
1154
- 'all_or_none_of works!'
1155
- end
1156
-
1157
- data = {
1158
- orders: [
1159
- { id: 77, drugs: { batch_no: 'A1234567', batch_id: '12' } },
1160
- { id: 70 }
1161
- ]
1162
- }
1163
-
1164
- get '/all_or_none_of', data
1165
- expect(last_response.body).to eq('all_or_none_of works!')
1166
- expect(last_response.status).to eq(200)
1167
- end
1168
- end
1169
-
1170
- context 'multiple validation errors' do
1171
- before do
1172
- subject.params do
1173
- requires :yolo
1174
- requires :swag
1175
- end
1176
- subject.get '/two_required' do
1177
- 'two required works'
1178
- end
1179
- end
1180
-
1181
- it 'throws the validation errors' do
1182
- get '/two_required'
1183
- expect(last_response.status).to eq(400)
1184
- expect(last_response.body).to match(/yolo is missing/)
1185
- expect(last_response.body).to match(/swag is missing/)
1186
- end
1187
- end
1188
-
1189
- context 'custom validation' do
1190
- let(:custom_validator) do
1191
- Class.new(Grape::Validations::Validators::Base) do
1192
- def validate_param!(attr_name, params)
1193
- return if params[attr_name] == 'im custom'
1194
-
1195
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: 'is not custom!')
1196
- end
1197
- end
1198
- end
1199
-
1200
- before do
1201
- described_class.register_validator('customvalidator', custom_validator)
1202
- end
1203
-
1204
- after do
1205
- described_class.deregister_validator('customvalidator')
1206
- end
1207
-
1208
- context 'when using optional with a custom validator' do
1209
- before do
1210
- subject.params do
1211
- optional :custom, customvalidator: true
1212
- end
1213
- subject.get '/optional_custom' do
1214
- 'optional with custom works!'
1215
- end
1216
- end
1217
-
1218
- it 'validates when param is present' do
1219
- get '/optional_custom', custom: 'im custom'
1220
- expect(last_response.status).to eq(200)
1221
- expect(last_response.body).to eq('optional with custom works!')
1222
-
1223
- get '/optional_custom', custom: 'im wrong'
1224
- expect(last_response.status).to eq(400)
1225
- expect(last_response.body).to eq('custom is not custom!')
1226
- end
1227
-
1228
- it "skips validation when parameter isn't present" do
1229
- get '/optional_custom'
1230
- expect(last_response.status).to eq(200)
1231
- expect(last_response.body).to eq('optional with custom works!')
1232
- end
1233
-
1234
- it 'validates with custom validator when param present and incorrect type' do
1235
- subject.params do
1236
- optional :custom, type: String, customvalidator: true
1237
- end
1238
-
1239
- get '/optional_custom', custom: 123
1240
- expect(last_response.status).to eq(400)
1241
- expect(last_response.body).to eq('custom is not custom!')
1242
- end
1243
- end
1244
-
1245
- context 'when using requires with a custom validator' do
1246
- before do
1247
- subject.params do
1248
- requires :custom, customvalidator: true
1249
- end
1250
- subject.get '/required_custom' do
1251
- 'required with custom works!'
1252
- end
1253
- end
1254
-
1255
- it 'validates when param is present' do
1256
- get '/required_custom', custom: 'im wrong, validate me'
1257
- expect(last_response.status).to eq(400)
1258
- expect(last_response.body).to eq('custom is not custom!')
1259
-
1260
- get '/required_custom', custom: 'im custom'
1261
- expect(last_response.status).to eq(200)
1262
- expect(last_response.body).to eq('required with custom works!')
1263
- end
1264
-
1265
- it 'validates when param is not present' do
1266
- get '/required_custom'
1267
- expect(last_response.status).to eq(400)
1268
- expect(last_response.body).to eq('custom is missing, custom is not custom!')
1269
- end
1270
-
1271
- context 'nested namespaces' do
1272
- before do
1273
- subject.params do
1274
- requires :custom, customvalidator: true
1275
- end
1276
- subject.namespace 'nested' do
1277
- get 'one' do
1278
- 'validation failed'
1279
- end
1280
- namespace 'nested' do
1281
- get 'two' do
1282
- 'validation failed'
1283
- end
1284
- end
1285
- end
1286
- subject.namespace 'peer' do
1287
- get 'one' do
1288
- 'no validation required'
1289
- end
1290
- namespace 'nested' do
1291
- get 'two' do
1292
- 'no validation required'
1293
- end
1294
- end
1295
- end
1296
-
1297
- subject.namespace 'unrelated' do
1298
- params do
1299
- requires :name
1300
- end
1301
- get 'one' do
1302
- 'validation required'
1303
- end
1304
-
1305
- namespace 'double' do
1306
- get 'two' do
1307
- 'no validation required'
1308
- end
1309
- end
1310
- end
1311
- end
1312
-
1313
- specify 'the parent namespace uses the validator' do
1314
- get '/nested/one', custom: 'im wrong, validate me'
1315
- expect(last_response.status).to eq(400)
1316
- expect(last_response.body).to eq('custom is not custom!')
1317
- end
1318
-
1319
- specify 'the nested namespace inherits the custom validator' do
1320
- get '/nested/nested/two', custom: 'im wrong, validate me'
1321
- expect(last_response.status).to eq(400)
1322
- expect(last_response.body).to eq('custom is not custom!')
1323
- end
1324
-
1325
- specify 'peer namespaces does not have the validator' do
1326
- get '/peer/one', custom: 'im not validated'
1327
- expect(last_response.status).to eq(200)
1328
- expect(last_response.body).to eq('no validation required')
1329
- end
1330
-
1331
- specify 'namespaces nested in peers should also not have the validator' do
1332
- get '/peer/nested/two', custom: 'im not validated'
1333
- expect(last_response.status).to eq(200)
1334
- expect(last_response.body).to eq('no validation required')
1335
- end
1336
-
1337
- specify 'when nested, specifying a route should clear out the validations for deeper nested params' do
1338
- get '/unrelated/one'
1339
- expect(last_response.status).to eq(400)
1340
- get '/unrelated/double/two'
1341
- expect(last_response.status).to eq(200)
1342
- end
1343
- end
1344
- end
1345
-
1346
- context 'when using options on param' do
1347
- let(:custom_validator_with_options) do
1348
- Class.new(Grape::Validations::Validators::Base) do
1349
- def validate_param!(attr_name, params)
1350
- return if params[attr_name] == @option[:text]
1351
-
1352
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
1353
- end
1354
- end
1355
- end
1356
-
1357
- before do
1358
- described_class.register_validator('customvalidator_with_options', custom_validator_with_options)
1359
- end
1360
-
1361
- after do
1362
- described_class.deregister_validator('customvalidator_with_options')
1363
- end
1364
-
1365
- before do
1366
- subject.params do
1367
- optional :custom, customvalidator_with_options: { text: 'im custom with options', message: 'is not custom with options!' }
1368
- end
1369
- subject.get '/optional_custom' do
1370
- 'optional with custom works!'
1371
- end
1372
- end
1373
-
1374
- it 'validates param with custom validator with options' do
1375
- get '/optional_custom', custom: 'im custom with options'
1376
- expect(last_response.status).to eq(200)
1377
- expect(last_response.body).to eq('optional with custom works!')
1378
-
1379
- get '/optional_custom', custom: 'im wrong'
1380
- expect(last_response.status).to eq(400)
1381
- expect(last_response.body).to eq('custom is not custom with options!')
1382
- end
1383
- end
1384
- end
1385
-
1386
- context 'named' do
1387
- context 'can be defined' do
1388
- it 'in helpers' do
1389
- subject.helpers do
1390
- params :pagination do
1391
- end
1392
- end
1393
- end
1394
-
1395
- it 'in helper module which kind of Grape::DSL::Helpers::BaseHelper' do
1396
- shared_params = Module.new do
1397
- extend Grape::DSL::Helpers::BaseHelper
1398
- params :pagination do
1399
- end
1400
- end
1401
- subject.helpers shared_params
1402
- end
1403
- end
1404
-
1405
- context 'can be included in usual params' do
1406
- before do
1407
- shared_params = Module.new do
1408
- extend Grape::DSL::Helpers::BaseHelper
1409
- params :period do
1410
- optional :start_date
1411
- optional :end_date
1412
- end
1413
- end
1414
-
1415
- subject.helpers shared_params
1416
-
1417
- subject.helpers do
1418
- params :pagination do
1419
- optional :page, type: Integer
1420
- optional :per_page, type: Integer
1421
- end
1422
- end
1423
- end
1424
-
1425
- it 'by #use' do
1426
- subject.params do
1427
- use :pagination
1428
- end
1429
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq %i[page per_page]
1430
- end
1431
-
1432
- it 'by #use with multiple params' do
1433
- subject.params do
1434
- use :pagination, :period
1435
- end
1436
- expect(Grape::Validations::ParamsScope::Attr.attrs_keys(declared_params)).to eq %i[page per_page start_date end_date]
1437
- end
1438
- end
1439
-
1440
- context 'with block' do
1441
- before do
1442
- subject.helpers do
1443
- params :order do |options|
1444
- optional :order, type: Symbol, values: %i[asc desc], default: options[:default_order]
1445
- optional :order_by, type: Symbol, values: options[:order_by], default: options[:default_order_by]
1446
- end
1447
- end
1448
- subject.format :json
1449
- subject.params do
1450
- use :order, default_order: :asc, order_by: %i[name created_at], default_order_by: :created_at
1451
- end
1452
- subject.get '/order' do
1453
- {
1454
- order: params[:order],
1455
- order_by: params[:order_by]
1456
- }
1457
- end
1458
- end
1459
-
1460
- it 'returns defaults' do
1461
- get '/order'
1462
- expect(last_response.status).to eq(200)
1463
- expect(last_response.body).to eq({ order: :asc, order_by: :created_at }.to_json)
1464
- end
1465
-
1466
- it 'overrides default value for order' do
1467
- get '/order?order=desc'
1468
- expect(last_response.status).to eq(200)
1469
- expect(last_response.body).to eq({ order: :desc, order_by: :created_at }.to_json)
1470
- end
1471
-
1472
- it 'overrides default value for order_by' do
1473
- get '/order?order_by=name'
1474
- expect(last_response.status).to eq(200)
1475
- expect(last_response.body).to eq({ order: :asc, order_by: :name }.to_json)
1476
- end
1477
-
1478
- it 'fails with invalid value' do
1479
- get '/order?order=invalid'
1480
- expect(last_response.status).to eq(400)
1481
- expect(last_response.body).to eq('{"error":"order does not have a valid value"}')
1482
- end
1483
- end
1484
- end
1485
-
1486
- context 'with block and keyword argument' do
1487
- before do
1488
- subject.helpers do
1489
- params :shared_params do |type:|
1490
- optional :param, default: type
1491
- end
1492
- end
1493
- subject.format :json
1494
- subject.params do
1495
- use :shared_params, type: 'value'
1496
- end
1497
- subject.get '/shared_params' do
1498
- {
1499
- param: params[:param]
1500
- }
1501
- end
1502
- end
1503
-
1504
- it 'works' do
1505
- get '/shared_params'
1506
-
1507
- expect(last_response.status).to eq(200)
1508
- expect(last_response.body).to eq({ param: 'value' }.to_json)
1509
- end
1510
- end
1511
-
1512
- context 'with block and empty args' do
1513
- before do
1514
- subject.helpers do
1515
- params :shared_params do |empty_args|
1516
- optional :param, default: empty_args[:some]
1517
- end
1518
- end
1519
- subject.format :json
1520
- subject.params do
1521
- use :shared_params
1522
- end
1523
- subject.get '/shared_params' do
1524
- :ok
1525
- end
1526
- end
1527
-
1528
- it 'works' do
1529
- get '/shared_params'
1530
-
1531
- expect(last_response.status).to eq(200)
1532
- end
1533
- end
1534
-
1535
- context 'all or none' do
1536
- context 'optional params' do
1537
- before do
1538
- subject.resource :custom_message do
1539
- params do
1540
- optional :beer
1541
- optional :wine
1542
- optional :juice
1543
- all_or_none_of :beer, :wine, :juice, message: 'all params are required or none is required'
1544
- end
1545
- get '/all_or_none' do
1546
- 'all_or_none works!'
1547
- end
1548
- end
1549
- end
1550
-
1551
- context 'with a custom validation message' do
1552
- it 'errors when any one is present' do
1553
- get '/custom_message/all_or_none', beer: 'string'
1554
- expect(last_response.status).to eq(400)
1555
- expect(last_response.body).to eq 'beer, wine, juice all params are required or none is required'
1556
- end
1557
-
1558
- it 'works when all params are present' do
1559
- get '/custom_message/all_or_none', beer: 'string', wine: 'anotherstring', juice: 'anotheranotherstring'
1560
- expect(last_response.status).to eq(200)
1561
- expect(last_response.body).to eq 'all_or_none works!'
1562
- end
1563
-
1564
- it 'works when none are present' do
1565
- get '/custom_message/all_or_none'
1566
- expect(last_response.status).to eq(200)
1567
- expect(last_response.body).to eq 'all_or_none works!'
1568
- end
1569
- end
1570
- end
1571
- end
1572
-
1573
- context 'mutually exclusive' do
1574
- context 'optional params' do
1575
- context 'with custom validation message' do
1576
- it 'errors when two or more are present' do
1577
- subject.resources :custom_message do
1578
- params do
1579
- optional :beer
1580
- optional :wine
1581
- optional :juice
1582
- mutually_exclusive :beer, :wine, :juice, message: 'are mutually exclusive cannot pass both params'
1583
- end
1584
- get '/mutually_exclusive' do
1585
- 'mutually_exclusive works!'
1586
- end
1587
- end
1588
- get '/custom_message/mutually_exclusive', beer: 'string', wine: 'anotherstring'
1589
- expect(last_response.status).to eq(400)
1590
- expect(last_response.body).to eq 'beer, wine are mutually exclusive cannot pass both params'
1591
- end
1592
- end
1593
-
1594
- it 'errors when two or more are present' do
1595
- subject.params do
1596
- optional :beer
1597
- optional :wine
1598
- optional :juice
1599
- mutually_exclusive :beer, :wine, :juice
1600
- end
1601
- subject.get '/mutually_exclusive' do
1602
- 'mutually_exclusive works!'
1603
- end
1604
-
1605
- get '/mutually_exclusive', beer: 'string', wine: 'anotherstring'
1606
- expect(last_response.status).to eq(400)
1607
- expect(last_response.body).to eq 'beer, wine are mutually exclusive'
1608
- end
1609
- end
1610
-
1611
- context 'more than one set of mutually exclusive params' do
1612
- context 'with a custom validation message' do
1613
- it 'errors for all sets' do
1614
- subject.resources :custom_message do
1615
- params do
1616
- optional :beer
1617
- optional :wine
1618
- mutually_exclusive :beer, :wine, message: 'are mutually exclusive pass only one'
1619
- optional :nested, type: Hash do
1620
- optional :scotch
1621
- optional :aquavit
1622
- mutually_exclusive :scotch, :aquavit, message: 'are mutually exclusive pass only one'
1623
- end
1624
- optional :nested2, type: Array do
1625
- optional :scotch2
1626
- optional :aquavit2
1627
- mutually_exclusive :scotch2, :aquavit2, message: 'are mutually exclusive pass only one'
1628
- end
1629
- end
1630
- get '/mutually_exclusive' do
1631
- 'mutually_exclusive works!'
1632
- end
1633
- end
1634
- get '/custom_message/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
1635
- expect(last_response.status).to eq(400)
1636
- expect(last_response.body).to eq(
1637
- 'beer, wine are mutually exclusive pass only one, nested[scotch], nested[aquavit] are mutually exclusive pass only one, nested2[1][scotch2], nested2[1][aquavit2] are mutually exclusive pass only one'
1638
- )
1639
- end
1640
- end
1641
-
1642
- it 'errors for all sets' do
1643
- subject.params do
1644
- optional :beer
1645
- optional :wine
1646
- mutually_exclusive :beer, :wine
1647
- optional :nested, type: Hash do
1648
- optional :scotch
1649
- optional :aquavit
1650
- mutually_exclusive :scotch, :aquavit
1651
- end
1652
- optional :nested2, type: Array do
1653
- optional :scotch2
1654
- optional :aquavit2
1655
- mutually_exclusive :scotch2, :aquavit2
1656
- end
1657
- end
1658
- subject.get '/mutually_exclusive' do
1659
- 'mutually_exclusive works!'
1660
- end
1661
-
1662
- get '/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
1663
- expect(last_response.status).to eq(400)
1664
- expect(last_response.body).to eq 'beer, wine are mutually exclusive, nested[scotch], nested[aquavit] are mutually exclusive, nested2[1][scotch2], nested2[1][aquavit2] are mutually exclusive'
1665
- end
1666
- end
1667
-
1668
- context 'in a group' do
1669
- it 'works when only one from the set is present' do
1670
- subject.params do
1671
- group :drink, type: Hash do
1672
- optional :wine
1673
- optional :beer
1674
- optional :juice
1675
- mutually_exclusive :beer, :wine, :juice
1676
- end
1677
- end
1678
- subject.get '/mutually_exclusive_group' do
1679
- 'mutually_exclusive_group works!'
1680
- end
1681
-
1682
- get '/mutually_exclusive_group', drink: { beer: 'true' }
1683
- expect(last_response.status).to eq(200)
1684
- end
1685
-
1686
- it 'errors when more than one from the set is present' do
1687
- subject.params do
1688
- group :drink, type: Hash do
1689
- optional :wine
1690
- optional :beer
1691
- optional :juice
1692
-
1693
- mutually_exclusive :beer, :wine, :juice
1694
- end
1695
- end
1696
- subject.get '/mutually_exclusive_group' do
1697
- 'mutually_exclusive_group works!'
1698
- end
1699
-
1700
- get '/mutually_exclusive_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
1701
- expect(last_response.status).to eq(400)
1702
- end
1703
- end
1704
-
1705
- context 'mutually exclusive params inside Hash group' do
1706
- it 'invalidates if request param is invalid type' do
1707
- subject.params do
1708
- optional :wine, type: Hash do
1709
- optional :grape
1710
- optional :country
1711
- mutually_exclusive :grape, :country
1712
- end
1713
- end
1714
- subject.post '/mutually_exclusive' do
1715
- 'mutually_exclusive works!'
1716
- end
1717
-
1718
- post '/mutually_exclusive', wine: '2015 sauvignon'
1719
- expect(last_response.status).to eq(400)
1720
- expect(last_response.body).to eq 'wine is invalid'
1721
- end
1722
- end
1723
- end
1724
-
1725
- context 'exactly one of' do
1726
- context 'params' do
1727
- before do
1728
- subject.resources :custom_message do
1729
- params do
1730
- optional :beer
1731
- optional :wine
1732
- optional :juice
1733
- exactly_one_of :beer, :wine, :juice, message: 'are missing, exactly one parameter is required'
1734
- end
1735
- get '/exactly_one_of' do
1736
- 'exactly_one_of works!'
1737
- end
1738
- end
1739
-
1740
- subject.params do
1741
- optional :beer
1742
- optional :wine
1743
- optional :juice
1744
- exactly_one_of :beer, :wine, :juice
1745
- end
1746
- subject.get '/exactly_one_of' do
1747
- 'exactly_one_of works!'
1748
- end
1749
- end
1750
-
1751
- context 'with a custom validation message' do
1752
- it 'errors when none are present' do
1753
- get '/custom_message/exactly_one_of'
1754
- expect(last_response.status).to eq(400)
1755
- expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter is required'
1756
- end
1757
-
1758
- it 'succeeds when one is present' do
1759
- get '/custom_message/exactly_one_of', beer: 'string'
1760
- expect(last_response.status).to eq(200)
1761
- expect(last_response.body).to eq 'exactly_one_of works!'
1762
- end
1763
-
1764
- it 'errors when two or more are present' do
1765
- get '/custom_message/exactly_one_of', beer: 'string', wine: 'anotherstring'
1766
- expect(last_response.status).to eq(400)
1767
- expect(last_response.body).to eq 'beer, wine are missing, exactly one parameter is required'
1768
- end
1769
- end
1770
-
1771
- it 'errors when none are present' do
1772
- get '/exactly_one_of'
1773
- expect(last_response.status).to eq(400)
1774
- expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter must be provided'
1775
- end
1776
-
1777
- it 'succeeds when one is present' do
1778
- get '/exactly_one_of', beer: 'string'
1779
- expect(last_response.status).to eq(200)
1780
- expect(last_response.body).to eq 'exactly_one_of works!'
1781
- end
1782
-
1783
- it 'errors when two or more are present' do
1784
- get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
1785
- expect(last_response.status).to eq(400)
1786
- expect(last_response.body).to eq 'beer, wine are mutually exclusive'
1787
- end
1788
- end
1789
-
1790
- context 'nested params' do
1791
- before do
1792
- subject.params do
1793
- requires :nested, type: Hash do
1794
- optional :beer_nested
1795
- optional :wine_nested
1796
- optional :juice_nested
1797
- exactly_one_of :beer_nested, :wine_nested, :juice_nested
1798
- end
1799
- optional :nested2, type: Array do
1800
- optional :beer_nested2
1801
- optional :wine_nested2
1802
- optional :juice_nested2
1803
- exactly_one_of :beer_nested2, :wine_nested2, :juice_nested2
1804
- end
1805
- end
1806
- subject.get '/exactly_one_of_nested' do
1807
- 'exactly_one_of works!'
1808
- end
1809
- end
1810
-
1811
- it 'errors when none are present' do
1812
- get '/exactly_one_of_nested'
1813
- expect(last_response.status).to eq(400)
1814
- expect(last_response.body).to eq 'nested is missing, nested[beer_nested], nested[wine_nested], nested[juice_nested] are missing, exactly one parameter must be provided'
1815
- end
1816
-
1817
- it 'succeeds when one is present' do
1818
- get '/exactly_one_of_nested', nested: { beer_nested: 'string' }
1819
- expect(last_response.status).to eq(200)
1820
- expect(last_response.body).to eq 'exactly_one_of works!'
1821
- end
1822
-
1823
- it 'errors when two or more are present' do
1824
- get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
1825
- expect(last_response.status).to eq(400)
1826
- expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2] are mutually exclusive'
1827
- end
1828
- end
1829
- end
1830
-
1831
- context 'at least one of' do
1832
- context 'params' do
1833
- before do
1834
- subject.resources :custom_message do
1835
- params do
1836
- optional :beer
1837
- optional :wine
1838
- optional :juice
1839
- at_least_one_of :beer, :wine, :juice, message: 'are missing, please specify at least one param'
1840
- end
1841
- get '/at_least_one_of' do
1842
- 'at_least_one_of works!'
1843
- end
1844
- end
1845
-
1846
- subject.params do
1847
- optional :beer
1848
- optional :wine
1849
- optional :juice
1850
- at_least_one_of :beer, :wine, :juice
1851
- end
1852
- subject.get '/at_least_one_of' do
1853
- 'at_least_one_of works!'
1854
- end
1855
- end
1856
-
1857
- context 'with a custom validation message' do
1858
- it 'errors when none are present' do
1859
- get '/custom_message/at_least_one_of'
1860
- expect(last_response.status).to eq(400)
1861
- expect(last_response.body).to eq 'beer, wine, juice are missing, please specify at least one param'
1862
- end
1863
-
1864
- it 'does not error when one is present' do
1865
- get '/custom_message/at_least_one_of', beer: 'string'
1866
- expect(last_response.status).to eq(200)
1867
- expect(last_response.body).to eq 'at_least_one_of works!'
1868
- end
1869
-
1870
- it 'does not error when two are present' do
1871
- get '/custom_message/at_least_one_of', beer: 'string', wine: 'string'
1872
- expect(last_response.status).to eq(200)
1873
- expect(last_response.body).to eq 'at_least_one_of works!'
1874
- end
1875
- end
1876
-
1877
- it 'errors when none are present' do
1878
- get '/at_least_one_of'
1879
- expect(last_response.status).to eq(400)
1880
- expect(last_response.body).to eq 'beer, wine, juice are missing, at least one parameter must be provided'
1881
- end
1882
-
1883
- it 'does not error when one is present' do
1884
- get '/at_least_one_of', beer: 'string'
1885
- expect(last_response.status).to eq(200)
1886
- expect(last_response.body).to eq 'at_least_one_of works!'
1887
- end
1888
-
1889
- it 'does not error when two are present' do
1890
- get '/at_least_one_of', beer: 'string', wine: 'string'
1891
- expect(last_response.status).to eq(200)
1892
- expect(last_response.body).to eq 'at_least_one_of works!'
1893
- end
1894
- end
1895
-
1896
- context 'nested params' do
1897
- before do
1898
- subject.params do
1899
- requires :nested, type: Hash do
1900
- optional :beer
1901
- optional :wine
1902
- optional :juice
1903
- at_least_one_of :beer, :wine, :juice
1904
- end
1905
- optional :nested2, type: Array do
1906
- optional :beer
1907
- optional :wine
1908
- optional :juice
1909
- at_least_one_of :beer, :wine, :juice
1910
- end
1911
- end
1912
- subject.get '/at_least_one_of_nested' do
1913
- 'at_least_one_of works!'
1914
- end
1915
- end
1916
-
1917
- it 'errors when none are present' do
1918
- get '/at_least_one_of_nested'
1919
- expect(last_response.status).to eq(400)
1920
- expect(last_response.body).to eq 'nested is missing, nested[beer], nested[wine], nested[juice] are missing, at least one parameter must be provided'
1921
- end
1922
-
1923
- it 'does not error when one is present' do
1924
- get '/at_least_one_of_nested', nested: { beer: 'string' }, nested2: [{ beer: 'string' }]
1925
- expect(last_response.status).to eq(200)
1926
- expect(last_response.body).to eq 'at_least_one_of works!'
1927
- end
1928
-
1929
- it 'does not error when two are present' do
1930
- get '/at_least_one_of_nested', nested: { beer: 'string', wine: 'string' }, nested2: [{ beer: 'string', wine: 'string' }]
1931
- expect(last_response.status).to eq(200)
1932
- expect(last_response.body).to eq 'at_least_one_of works!'
1933
- end
1934
- end
1935
- end
1936
-
1937
- context 'in a group' do
1938
- it 'works when only one from the set is present' do
1939
- subject.params do
1940
- group :drink, type: Hash do
1941
- optional :wine
1942
- optional :beer
1943
- optional :juice
1944
-
1945
- exactly_one_of :beer, :wine, :juice
1946
- end
1947
- end
1948
- subject.get '/exactly_one_of_group' do
1949
- 'exactly_one_of_group works!'
1950
- end
1951
-
1952
- get '/exactly_one_of_group', drink: { beer: 'true' }
1953
- expect(last_response.status).to eq(200)
1954
- end
1955
-
1956
- it 'errors when no parameter from the set is present' do
1957
- subject.params do
1958
- group :drink, type: Hash do
1959
- optional :wine
1960
- optional :beer
1961
- optional :juice
1962
-
1963
- exactly_one_of :beer, :wine, :juice
1964
- end
1965
- end
1966
- subject.get '/exactly_one_of_group' do
1967
- 'exactly_one_of_group works!'
1968
- end
1969
-
1970
- get '/exactly_one_of_group', drink: {}
1971
- expect(last_response.status).to eq(400)
1972
- end
1973
-
1974
- it 'errors when more than one from the set is present' do
1975
- subject.params do
1976
- group :drink, type: Hash do
1977
- optional :wine
1978
- optional :beer
1979
- optional :juice
1980
-
1981
- exactly_one_of :beer, :wine, :juice
1982
- end
1983
- end
1984
- subject.get '/exactly_one_of_group' do
1985
- 'exactly_one_of_group works!'
1986
- end
1987
-
1988
- get '/exactly_one_of_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
1989
- expect(last_response.status).to eq(400)
1990
- end
1991
-
1992
- it 'does not falsely think the param is there if it is provided outside the block' do
1993
- subject.params do
1994
- group :drink, type: Hash do
1995
- optional :wine
1996
- optional :beer
1997
- optional :juice
1998
-
1999
- exactly_one_of :beer, :wine, :juice
2000
- end
2001
- end
2002
- subject.get '/exactly_one_of_group' do
2003
- 'exactly_one_of_group works!'
2004
- end
2005
-
2006
- get '/exactly_one_of_group', drink: { foo: 'bar' }, beer: 'true'
2007
- expect(last_response.status).to eq(400)
2008
- end
2009
- end
2010
- end
2011
-
2012
- describe 'require_validator' do
2013
- subject { described_class.require_validator(short_name) }
2014
-
2015
- context 'when found' do
2016
- let(:short_name) { :presence }
2017
-
2018
- it { is_expected.to be(Grape::Validations::Validators::PresenceValidator) }
2019
- end
2020
-
2021
- context 'when not found' do
2022
- let(:short_name) { :test }
2023
-
2024
- it 'raises an error' do
2025
- expect { subject }.to raise_error(Grape::Exceptions::UnknownValidator)
2026
- end
2027
- end
2028
- end
2029
- end