grape 1.7.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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