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