grape 0.7.0 → 0.8.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.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +5 -13
- data/.rubocop.yml +6 -6
- data/.travis.yml +11 -2
- data/CHANGELOG.md +23 -1
- data/Gemfile +11 -10
- data/Guardfile +5 -6
- data/README.md +194 -13
- data/UPGRADING.md +3 -3
- data/grape.gemspec +1 -1
- data/grape.png +0 -0
- data/lib/grape/api.rb +21 -13
- data/lib/grape/endpoint.rb +31 -13
- data/lib/grape/exceptions/validation.rb +2 -2
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/oauth2.rb +69 -65
- data/lib/grape/middleware/error.rb +4 -2
- data/lib/grape/middleware/formatter.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +3 -3
- data/lib/grape/util/hash_stack.rb +1 -1
- data/lib/grape/validations.rb +22 -8
- data/lib/grape/validations/default.rb +1 -1
- data/lib/grape/validations/exactly_one_of.rb +26 -0
- data/lib/grape/validations/mutual_exclusion.rb +25 -0
- data/lib/grape/validations/presence.rb +1 -1
- data/lib/grape/validations/regexp.rb +2 -1
- data/lib/grape/validations/values.rb +7 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +390 -333
- data/spec/grape/endpoint_spec.rb +129 -99
- data/spec/grape/entity_spec.rb +47 -27
- data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +1 -1
- data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -2
- data/spec/grape/exceptions/missing_option_spec.rb +1 -1
- data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
- data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
- data/spec/grape/middleware/auth/basic_spec.rb +3 -3
- data/spec/grape/middleware/auth/digest_spec.rb +4 -4
- data/spec/grape/middleware/auth/oauth2_spec.rb +11 -11
- data/spec/grape/middleware/base_spec.rb +9 -9
- data/spec/grape/middleware/error_spec.rb +4 -4
- data/spec/grape/middleware/exception_spec.rb +17 -17
- data/spec/grape/middleware/formatter_spec.rb +38 -38
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +11 -11
- data/spec/grape/middleware/versioner/header_spec.rb +39 -39
- data/spec/grape/middleware/versioner/param_spec.rb +10 -10
- data/spec/grape/middleware/versioner/path_spec.rb +7 -7
- data/spec/grape/middleware/versioner_spec.rb +4 -4
- data/spec/grape/path_spec.rb +6 -6
- data/spec/grape/util/hash_stack_spec.rb +22 -22
- data/spec/grape/validations/coerce_spec.rb +34 -34
- data/spec/grape/validations/default_spec.rb +16 -16
- data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
- data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
- data/spec/grape/validations/presence_spec.rb +34 -34
- data/spec/grape/validations/regexp_spec.rb +11 -4
- data/spec/grape/validations/values_spec.rb +34 -20
- data/spec/grape/validations_spec.rb +300 -147
- data/spec/shared/versioning_examples.rb +18 -18
- data/spec/spec_helper.rb +2 -1
- metadata +81 -38
@@ -19,12 +19,12 @@ describe Grape::Validations do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
get '/optional', a_number: 'string'
|
22
|
-
last_response.status.
|
23
|
-
last_response.body.
|
22
|
+
expect(last_response.status).to eq(400)
|
23
|
+
expect(last_response.body).to eq('a_number is invalid')
|
24
24
|
|
25
25
|
get '/optional', a_number: 45
|
26
|
-
last_response.status.
|
27
|
-
last_response.body.
|
26
|
+
expect(last_response.status).to eq(200)
|
27
|
+
expect(last_response.body).to eq('optional works!')
|
28
28
|
end
|
29
29
|
|
30
30
|
it "doesn't validate when param not present" do
|
@@ -36,15 +36,15 @@ describe Grape::Validations do
|
|
36
36
|
end
|
37
37
|
|
38
38
|
get '/optional'
|
39
|
-
last_response.status.
|
40
|
-
last_response.body.
|
39
|
+
expect(last_response.status).to eq(200)
|
40
|
+
expect(last_response.body).to eq('optional works!')
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'adds to declared parameters' do
|
44
44
|
subject.params do
|
45
45
|
optional :some_param
|
46
46
|
end
|
47
|
-
subject.settings[:declared_params].
|
47
|
+
expect(subject.settings[:declared_params]).to eq([:some_param])
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -60,29 +60,29 @@ describe Grape::Validations do
|
|
60
60
|
|
61
61
|
it 'errors when param not present' do
|
62
62
|
get '/required'
|
63
|
-
last_response.status.
|
64
|
-
last_response.body.
|
63
|
+
expect(last_response.status).to eq(400)
|
64
|
+
expect(last_response.body).to eq('key is missing')
|
65
65
|
end
|
66
66
|
|
67
67
|
it "doesn't throw a missing param when param is present" do
|
68
68
|
get '/required', key: 'cool'
|
69
|
-
last_response.status.
|
70
|
-
last_response.body.
|
69
|
+
expect(last_response.status).to eq(200)
|
70
|
+
expect(last_response.body).to eq('required works')
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'adds to declared parameters' do
|
74
74
|
subject.params do
|
75
75
|
requires :some_param
|
76
76
|
end
|
77
|
-
subject.settings[:declared_params].
|
77
|
+
expect(subject.settings[:declared_params]).to eq([:some_param])
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
81
|
context 'requires :all using Grape::Entity documentation' do
|
82
82
|
def define_requires_all
|
83
83
|
documentation = {
|
84
|
-
|
85
|
-
|
84
|
+
required_field: { type: String },
|
85
|
+
optional_field: { type: String }
|
86
86
|
}
|
87
87
|
subject.params do
|
88
88
|
requires :all, except: :optional_field, using: documentation
|
@@ -97,27 +97,27 @@ describe Grape::Validations do
|
|
97
97
|
|
98
98
|
it 'adds entity documentation to declared params' do
|
99
99
|
define_requires_all
|
100
|
-
subject.settings[:declared_params].
|
100
|
+
expect(subject.settings[:declared_params]).to eq([:required_field, :optional_field])
|
101
101
|
end
|
102
102
|
|
103
103
|
it 'errors when required_field is not present' do
|
104
104
|
get '/required'
|
105
|
-
last_response.status.
|
106
|
-
last_response.body.
|
105
|
+
expect(last_response.status).to eq(400)
|
106
|
+
expect(last_response.body).to eq('required_field is missing')
|
107
107
|
end
|
108
108
|
|
109
109
|
it 'works when required_field is present' do
|
110
110
|
get '/required', required_field: 'woof'
|
111
|
-
last_response.status.
|
112
|
-
last_response.body.
|
111
|
+
expect(last_response.status).to eq(200)
|
112
|
+
expect(last_response.body).to eq('required works')
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
116
|
context 'requires :none using Grape::Entity documentation' do
|
117
117
|
def define_requires_none
|
118
118
|
documentation = {
|
119
|
-
|
120
|
-
|
119
|
+
required_field: { type: String },
|
120
|
+
optional_field: { type: String }
|
121
121
|
}
|
122
122
|
subject.params do
|
123
123
|
requires :none, except: :required_field, using: documentation
|
@@ -132,19 +132,54 @@ describe Grape::Validations do
|
|
132
132
|
|
133
133
|
it 'adds entity documentation to declared params' do
|
134
134
|
define_requires_none
|
135
|
-
subject.settings[:declared_params].
|
135
|
+
expect(subject.settings[:declared_params]).to eq([:required_field, :optional_field])
|
136
136
|
end
|
137
137
|
|
138
138
|
it 'errors when required_field is not present' do
|
139
139
|
get '/required'
|
140
|
-
last_response.status.
|
141
|
-
last_response.body.
|
140
|
+
expect(last_response.status).to eq(400)
|
141
|
+
expect(last_response.body).to eq('required_field is missing')
|
142
142
|
end
|
143
143
|
|
144
144
|
it 'works when required_field is present' do
|
145
145
|
get '/required', required_field: 'woof'
|
146
|
-
last_response.status.
|
147
|
-
last_response.body.
|
146
|
+
expect(last_response.status).to eq(200)
|
147
|
+
expect(last_response.body).to eq('required works')
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'requires :all or :none but except a non-existent field using Grape::Entity documentation' do
|
152
|
+
context 'requires :all' do
|
153
|
+
def define_requires_all
|
154
|
+
documentation = {
|
155
|
+
required_field: { type: String },
|
156
|
+
optional_field: { type: String }
|
157
|
+
}
|
158
|
+
subject.params do
|
159
|
+
requires :all, except: :non_existent_field, using: documentation
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'adds only the entity documentation to declared params, nothing more' do
|
164
|
+
define_requires_all
|
165
|
+
expect(subject.settings[:declared_params]).to eq([:required_field, :optional_field])
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'requires :none' do
|
170
|
+
def define_requires_none
|
171
|
+
documentation = {
|
172
|
+
required_field: { type: String },
|
173
|
+
optional_field: { type: String }
|
174
|
+
}
|
175
|
+
subject.params do
|
176
|
+
requires :none, except: :non_existent_field, using: documentation
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'adds only the entity documentation to declared params, nothing more' do
|
181
|
+
expect { define_requires_none }.to raise_error(ArgumentError)
|
182
|
+
end
|
148
183
|
end
|
149
184
|
end
|
150
185
|
|
@@ -162,24 +197,24 @@ describe Grape::Validations do
|
|
162
197
|
|
163
198
|
it 'errors when param not present' do
|
164
199
|
get '/required'
|
165
|
-
last_response.status.
|
166
|
-
last_response.body.
|
200
|
+
expect(last_response.status).to eq(400)
|
201
|
+
expect(last_response.body).to eq('items is missing')
|
167
202
|
end
|
168
203
|
|
169
204
|
it "errors when param is not an Array" do
|
170
205
|
get '/required', items: "hello"
|
171
|
-
last_response.status.
|
172
|
-
last_response.body.
|
206
|
+
expect(last_response.status).to eq(400)
|
207
|
+
expect(last_response.body).to eq('items is invalid, items[key] is missing')
|
173
208
|
|
174
209
|
get '/required', items: { key: 'foo' }
|
175
|
-
last_response.status.
|
176
|
-
last_response.body.
|
210
|
+
expect(last_response.status).to eq(400)
|
211
|
+
expect(last_response.body).to eq('items is invalid')
|
177
212
|
end
|
178
213
|
|
179
214
|
it "doesn't throw a missing param when param is present" do
|
180
215
|
get '/required', items: [{ key: 'hello' }, { key: 'world' }]
|
181
|
-
last_response.status.
|
182
|
-
last_response.body.
|
216
|
+
expect(last_response.status).to eq(200)
|
217
|
+
expect(last_response.body).to eq('required works')
|
183
218
|
end
|
184
219
|
|
185
220
|
it "doesn't allow any key in the options hash other than type" do
|
@@ -198,7 +233,7 @@ describe Grape::Validations do
|
|
198
233
|
requires :key
|
199
234
|
end
|
200
235
|
end
|
201
|
-
subject.settings[:declared_params].
|
236
|
+
expect(subject.settings[:declared_params]).to eq([items: [:key]])
|
202
237
|
end
|
203
238
|
end
|
204
239
|
|
@@ -216,24 +251,24 @@ describe Grape::Validations do
|
|
216
251
|
|
217
252
|
it 'errors when param not present' do
|
218
253
|
get '/required'
|
219
|
-
last_response.status.
|
220
|
-
last_response.body.
|
254
|
+
expect(last_response.status).to eq(400)
|
255
|
+
expect(last_response.body).to eq('items is missing, items[key] is missing')
|
221
256
|
end
|
222
257
|
|
223
258
|
it "errors when param is not a Hash" do
|
224
259
|
get '/required', items: "hello"
|
225
|
-
last_response.status.
|
226
|
-
last_response.body.
|
260
|
+
expect(last_response.status).to eq(400)
|
261
|
+
expect(last_response.body).to eq('items is invalid, items[key] is missing')
|
227
262
|
|
228
263
|
get '/required', items: [{ key: 'foo' }]
|
229
|
-
last_response.status.
|
230
|
-
last_response.body.
|
264
|
+
expect(last_response.status).to eq(400)
|
265
|
+
expect(last_response.body).to eq('items is invalid')
|
231
266
|
end
|
232
267
|
|
233
268
|
it "doesn't throw a missing param when param is present" do
|
234
269
|
get '/required', items: { key: 'hello' }
|
235
|
-
last_response.status.
|
236
|
-
last_response.body.
|
270
|
+
expect(last_response.status).to eq(200)
|
271
|
+
expect(last_response.body).to eq('required works')
|
237
272
|
end
|
238
273
|
|
239
274
|
it "doesn't allow any key in the options hash other than type" do
|
@@ -252,7 +287,7 @@ describe Grape::Validations do
|
|
252
287
|
requires :key
|
253
288
|
end
|
254
289
|
end
|
255
|
-
subject.settings[:declared_params].
|
290
|
+
expect(subject.settings[:declared_params]).to eq([items: [:key]])
|
256
291
|
end
|
257
292
|
end
|
258
293
|
|
@@ -270,14 +305,14 @@ describe Grape::Validations do
|
|
270
305
|
|
271
306
|
it 'errors when param not present' do
|
272
307
|
get '/required'
|
273
|
-
last_response.status.
|
274
|
-
last_response.body.
|
308
|
+
expect(last_response.status).to eq(400)
|
309
|
+
expect(last_response.body).to eq('items is missing')
|
275
310
|
end
|
276
311
|
|
277
312
|
it "doesn't throw a missing param when param is present" do
|
278
313
|
get '/required', items: [key: 'hello', key: 'world']
|
279
|
-
last_response.status.
|
280
|
-
last_response.body.
|
314
|
+
expect(last_response.status).to eq(200)
|
315
|
+
expect(last_response.body).to eq('required works')
|
281
316
|
end
|
282
317
|
|
283
318
|
it 'adds to declared parameters' do
|
@@ -286,7 +321,7 @@ describe Grape::Validations do
|
|
286
321
|
requires :key
|
287
322
|
end
|
288
323
|
end
|
289
|
-
subject.settings[:declared_params].
|
324
|
+
expect(subject.settings[:declared_params]).to eq([items: [:key]])
|
290
325
|
end
|
291
326
|
end
|
292
327
|
|
@@ -310,8 +345,8 @@ describe Grape::Validations do
|
|
310
345
|
{ name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
|
311
346
|
{ name: 'Joe', parents: [{ name: 'Josie' }] }
|
312
347
|
]
|
313
|
-
last_response.status.
|
314
|
-
last_response.body.
|
348
|
+
expect(last_response.status).to eq(200)
|
349
|
+
expect(last_response.body).to eq('within array works')
|
315
350
|
end
|
316
351
|
|
317
352
|
it 'errors when a parameter is not present' do
|
@@ -321,34 +356,34 @@ describe Grape::Validations do
|
|
321
356
|
]
|
322
357
|
# NOTE: with body parameters in json or XML or similar this
|
323
358
|
# should actually fail with: children[parents][name] is missing.
|
324
|
-
last_response.status.
|
325
|
-
last_response.body.
|
359
|
+
expect(last_response.status).to eq(400)
|
360
|
+
expect(last_response.body).to eq('children[parents] is missing')
|
326
361
|
end
|
327
362
|
|
328
363
|
it 'safely handles empty arrays and blank parameters' do
|
329
364
|
# NOTE: with body parameters in json or XML or similar this
|
330
365
|
# should actually return 200, since an empty array is valid.
|
331
366
|
get '/within_array', children: []
|
332
|
-
last_response.status.
|
333
|
-
last_response.body.
|
367
|
+
expect(last_response.status).to eq(400)
|
368
|
+
expect(last_response.body).to eq('children is missing')
|
334
369
|
get '/within_array', children: [name: 'Jay']
|
335
|
-
last_response.status.
|
336
|
-
last_response.body.
|
370
|
+
expect(last_response.status).to eq(400)
|
371
|
+
expect(last_response.body).to eq('children[parents] is missing')
|
337
372
|
end
|
338
373
|
|
339
374
|
it "errors when param is not an Array" do
|
340
375
|
# NOTE: would be nicer if these just returned 'children is invalid'
|
341
376
|
get '/within_array', children: "hello"
|
342
|
-
last_response.status.
|
343
|
-
last_response.body.
|
377
|
+
expect(last_response.status).to eq(400)
|
378
|
+
expect(last_response.body).to eq('children is invalid, children[name] is missing, children[parents] is missing, children[parents] is invalid, children[parents][name] is missing')
|
344
379
|
|
345
380
|
get '/within_array', children: { name: 'foo' }
|
346
|
-
last_response.status.
|
347
|
-
last_response.body.
|
381
|
+
expect(last_response.status).to eq(400)
|
382
|
+
expect(last_response.body).to eq('children is invalid, children[parents] is missing')
|
348
383
|
|
349
384
|
get '/within_array', children: [name: 'Jay', parents: { name: 'Fred' }]
|
350
|
-
last_response.status.
|
351
|
-
last_response.body.
|
385
|
+
expect(last_response.status).to eq(400)
|
386
|
+
expect(last_response.body).to eq('children[parents] is invalid')
|
352
387
|
end
|
353
388
|
end
|
354
389
|
|
@@ -394,53 +429,53 @@ describe Grape::Validations do
|
|
394
429
|
|
395
430
|
it 'requires defaults to Array type' do
|
396
431
|
get '/req', planets: "Jupiter, Saturn"
|
397
|
-
last_response.status.
|
398
|
-
last_response.body.
|
432
|
+
expect(last_response.status).to eq(400)
|
433
|
+
expect(last_response.body).to eq('planets is invalid, planets[name] is missing')
|
399
434
|
|
400
435
|
get '/req', planets: { name: 'Jupiter' }
|
401
|
-
last_response.status.
|
402
|
-
last_response.body.
|
436
|
+
expect(last_response.status).to eq(400)
|
437
|
+
expect(last_response.body).to eq('planets is invalid')
|
403
438
|
|
404
439
|
get '/req', planets: [{ name: 'Venus' }, { name: 'Mars' }]
|
405
|
-
last_response.status.
|
440
|
+
expect(last_response.status).to eq(200)
|
406
441
|
|
407
442
|
put_with_json '/req', planets: []
|
408
|
-
last_response.status.
|
443
|
+
expect(last_response.status).to eq(200)
|
409
444
|
end
|
410
445
|
|
411
446
|
it 'optional defaults to Array type' do
|
412
447
|
get '/opt', name: "Jupiter", moons: "Europa, Ganymede"
|
413
|
-
last_response.status.
|
414
|
-
last_response.body.
|
448
|
+
expect(last_response.status).to eq(400)
|
449
|
+
expect(last_response.body).to eq('moons is invalid, moons[name] is missing')
|
415
450
|
|
416
451
|
get '/opt', name: "Jupiter", moons: { name: 'Ganymede' }
|
417
|
-
last_response.status.
|
418
|
-
last_response.body.
|
452
|
+
expect(last_response.status).to eq(400)
|
453
|
+
expect(last_response.body).to eq('moons is invalid')
|
419
454
|
|
420
455
|
get '/opt', name: "Jupiter", moons: [{ name: 'Io' }, { name: 'Callisto' }]
|
421
|
-
last_response.status.
|
456
|
+
expect(last_response.status).to eq(200)
|
422
457
|
|
423
458
|
put_with_json '/opt', name: "Venus"
|
424
|
-
last_response.status.
|
459
|
+
expect(last_response.status).to eq(200)
|
425
460
|
|
426
461
|
put_with_json '/opt', name: "Mercury", moons: []
|
427
|
-
last_response.status.
|
462
|
+
expect(last_response.status).to eq(200)
|
428
463
|
end
|
429
464
|
|
430
465
|
it 'group defaults to Array type' do
|
431
466
|
get '/grp', stars: "Sun"
|
432
|
-
last_response.status.
|
433
|
-
last_response.body.
|
467
|
+
expect(last_response.status).to eq(400)
|
468
|
+
expect(last_response.body).to eq('stars is invalid, stars[name] is missing')
|
434
469
|
|
435
470
|
get '/grp', stars: { name: 'Sun' }
|
436
|
-
last_response.status.
|
437
|
-
last_response.body.
|
471
|
+
expect(last_response.status).to eq(400)
|
472
|
+
expect(last_response.body).to eq('stars is invalid')
|
438
473
|
|
439
474
|
get '/grp', stars: [{ name: 'Sun' }]
|
440
|
-
last_response.status.
|
475
|
+
expect(last_response.status).to eq(200)
|
441
476
|
|
442
477
|
put_with_json '/grp', stars: []
|
443
|
-
last_response.status.
|
478
|
+
expect(last_response.status).to eq(200)
|
444
479
|
end
|
445
480
|
end
|
446
481
|
|
@@ -464,8 +499,8 @@ describe Grape::Validations do
|
|
464
499
|
{ name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
|
465
500
|
{ name: 'Joe', parents: [{ name: 'Josie' }] }
|
466
501
|
]
|
467
|
-
last_response.status.
|
468
|
-
last_response.body.
|
502
|
+
expect(last_response.status).to eq(200)
|
503
|
+
expect(last_response.body).to eq('within array works')
|
469
504
|
end
|
470
505
|
|
471
506
|
it 'errors when a parameter is not present' do
|
@@ -473,16 +508,16 @@ describe Grape::Validations do
|
|
473
508
|
{ name: 'Jim', parents: [{}] },
|
474
509
|
{ name: 'Job', parents: [{ name: 'Joy' }] }
|
475
510
|
]
|
476
|
-
last_response.status.
|
477
|
-
last_response.body.
|
511
|
+
expect(last_response.status).to eq(400)
|
512
|
+
expect(last_response.body).to eq('children[parents][name] is missing')
|
478
513
|
end
|
479
514
|
|
480
515
|
it 'safely handles empty arrays and blank parameters' do
|
481
516
|
put_with_json '/within_array', children: []
|
482
|
-
last_response.status.
|
517
|
+
expect(last_response.status).to eq(200)
|
483
518
|
put_with_json '/within_array', children: [name: 'Jay']
|
484
|
-
last_response.status.
|
485
|
-
last_response.body.
|
519
|
+
expect(last_response.status).to eq(400)
|
520
|
+
expect(last_response.body).to eq('children[parents] is missing')
|
486
521
|
end
|
487
522
|
end
|
488
523
|
|
@@ -500,30 +535,30 @@ describe Grape::Validations do
|
|
500
535
|
|
501
536
|
it "doesn't throw a missing param when the group isn't present" do
|
502
537
|
get '/optional_group'
|
503
|
-
last_response.status.
|
504
|
-
last_response.body.
|
538
|
+
expect(last_response.status).to eq(200)
|
539
|
+
expect(last_response.body).to eq('optional group works')
|
505
540
|
end
|
506
541
|
|
507
542
|
it "doesn't throw a missing param when both group and param are given" do
|
508
543
|
get '/optional_group', items: [{ key: 'foo' }]
|
509
|
-
last_response.status.
|
510
|
-
last_response.body.
|
544
|
+
expect(last_response.status).to eq(200)
|
545
|
+
expect(last_response.body).to eq('optional group works')
|
511
546
|
end
|
512
547
|
|
513
548
|
it "errors when group is present, but required param is not" do
|
514
549
|
get '/optional_group', items: [{ not_key: 'foo' }]
|
515
|
-
last_response.status.
|
516
|
-
last_response.body.
|
550
|
+
expect(last_response.status).to eq(400)
|
551
|
+
expect(last_response.body).to eq('items[key] is missing')
|
517
552
|
end
|
518
553
|
|
519
554
|
it "errors when param is present but isn't an Array" do
|
520
555
|
get '/optional_group', items: "hello"
|
521
|
-
last_response.status.
|
522
|
-
last_response.body.
|
556
|
+
expect(last_response.status).to eq(400)
|
557
|
+
expect(last_response.body).to eq('items is invalid, items[key] is missing')
|
523
558
|
|
524
559
|
get '/optional_group', items: { key: 'foo' }
|
525
|
-
last_response.status.
|
526
|
-
last_response.body.
|
560
|
+
expect(last_response.status).to eq(400)
|
561
|
+
expect(last_response.body).to eq('items is invalid')
|
527
562
|
end
|
528
563
|
|
529
564
|
it 'adds to declared parameters' do
|
@@ -532,7 +567,7 @@ describe Grape::Validations do
|
|
532
567
|
requires :key
|
533
568
|
end
|
534
569
|
end
|
535
|
-
subject.settings[:declared_params].
|
570
|
+
expect(subject.settings[:declared_params]).to eq([items: [:key]])
|
536
571
|
end
|
537
572
|
end
|
538
573
|
|
@@ -550,42 +585,42 @@ describe Grape::Validations do
|
|
550
585
|
|
551
586
|
it 'does no internal validations if the outer group is blank' do
|
552
587
|
get '/nested_optional_group'
|
553
|
-
last_response.status.
|
554
|
-
last_response.body.
|
588
|
+
expect(last_response.status).to eq(200)
|
589
|
+
expect(last_response.body).to eq('nested optional group works')
|
555
590
|
end
|
556
591
|
|
557
592
|
it 'does internal validations if the outer group is present' do
|
558
593
|
get '/nested_optional_group', items: [{ key: 'foo' }]
|
559
|
-
last_response.status.
|
560
|
-
last_response.body.
|
594
|
+
expect(last_response.status).to eq(400)
|
595
|
+
expect(last_response.body).to eq('items[required_subitems] is missing')
|
561
596
|
|
562
597
|
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
|
563
|
-
last_response.status.
|
564
|
-
last_response.body.
|
598
|
+
expect(last_response.status).to eq(200)
|
599
|
+
expect(last_response.body).to eq('nested optional group works')
|
565
600
|
end
|
566
601
|
|
567
602
|
it 'handles deep nesting' do
|
568
603
|
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
|
569
|
-
last_response.status.
|
570
|
-
last_response.body.
|
604
|
+
expect(last_response.status).to eq(400)
|
605
|
+
expect(last_response.body).to eq('items[optional_subitems][value] is missing')
|
571
606
|
|
572
607
|
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ value: 'baz' }] }]
|
573
|
-
last_response.status.
|
574
|
-
last_response.body.
|
608
|
+
expect(last_response.status).to eq(200)
|
609
|
+
expect(last_response.body).to eq('nested optional group works')
|
575
610
|
end
|
576
611
|
|
577
612
|
it 'handles validation within arrays' do
|
578
613
|
get '/nested_optional_group', items: [{ key: 'foo' }]
|
579
|
-
last_response.status.
|
580
|
-
last_response.body.
|
614
|
+
expect(last_response.status).to eq(400)
|
615
|
+
expect(last_response.body).to eq('items[required_subitems] is missing')
|
581
616
|
|
582
617
|
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
|
583
|
-
last_response.status.
|
584
|
-
last_response.body.
|
618
|
+
expect(last_response.status).to eq(200)
|
619
|
+
expect(last_response.body).to eq('nested optional group works')
|
585
620
|
|
586
621
|
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
|
587
|
-
last_response.status.
|
588
|
-
last_response.body.
|
622
|
+
expect(last_response.status).to eq(400)
|
623
|
+
expect(last_response.body).to eq('items[optional_subitems][value] is missing')
|
589
624
|
end
|
590
625
|
|
591
626
|
it 'adds to declared parameters' do
|
@@ -596,7 +631,7 @@ describe Grape::Validations do
|
|
596
631
|
requires(:required_subitems) { requires :value }
|
597
632
|
end
|
598
633
|
end
|
599
|
-
subject.settings[:declared_params].
|
634
|
+
expect(subject.settings[:declared_params]).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
|
600
635
|
end
|
601
636
|
end
|
602
637
|
|
@@ -613,9 +648,9 @@ describe Grape::Validations do
|
|
613
648
|
|
614
649
|
it 'throws the validation errors' do
|
615
650
|
get '/two_required'
|
616
|
-
last_response.status.
|
617
|
-
last_response.body.
|
618
|
-
last_response.body.
|
651
|
+
expect(last_response.status).to eq(400)
|
652
|
+
expect(last_response.body).to match(/yolo is missing/)
|
653
|
+
expect(last_response.body).to match(/swag is missing/)
|
619
654
|
end
|
620
655
|
end
|
621
656
|
|
@@ -642,18 +677,18 @@ describe Grape::Validations do
|
|
642
677
|
|
643
678
|
it 'validates when param is present' do
|
644
679
|
get '/optional_custom', custom: 'im custom'
|
645
|
-
last_response.status.
|
646
|
-
last_response.body.
|
680
|
+
expect(last_response.status).to eq(200)
|
681
|
+
expect(last_response.body).to eq('optional with custom works!')
|
647
682
|
|
648
683
|
get '/optional_custom', custom: 'im wrong'
|
649
|
-
last_response.status.
|
650
|
-
last_response.body.
|
684
|
+
expect(last_response.status).to eq(400)
|
685
|
+
expect(last_response.body).to eq('custom is not custom!')
|
651
686
|
end
|
652
687
|
|
653
688
|
it "skips validation when parameter isn't present" do
|
654
689
|
get '/optional_custom'
|
655
|
-
last_response.status.
|
656
|
-
last_response.body.
|
690
|
+
expect(last_response.status).to eq(200)
|
691
|
+
expect(last_response.body).to eq('optional with custom works!')
|
657
692
|
end
|
658
693
|
|
659
694
|
it 'validates with custom validator when param present and incorrect type' do
|
@@ -662,8 +697,8 @@ describe Grape::Validations do
|
|
662
697
|
end
|
663
698
|
|
664
699
|
get '/optional_custom', custom: 123
|
665
|
-
last_response.status.
|
666
|
-
last_response.body.
|
700
|
+
expect(last_response.status).to eq(400)
|
701
|
+
expect(last_response.body).to eq('custom is not custom!')
|
667
702
|
end
|
668
703
|
end
|
669
704
|
|
@@ -679,18 +714,18 @@ describe Grape::Validations do
|
|
679
714
|
|
680
715
|
it 'validates when param is present' do
|
681
716
|
get '/required_custom', custom: 'im wrong, validate me'
|
682
|
-
last_response.status.
|
683
|
-
last_response.body.
|
717
|
+
expect(last_response.status).to eq(400)
|
718
|
+
expect(last_response.body).to eq('custom is not custom!')
|
684
719
|
|
685
720
|
get '/required_custom', custom: 'im custom'
|
686
|
-
last_response.status.
|
687
|
-
last_response.body.
|
721
|
+
expect(last_response.status).to eq(200)
|
722
|
+
expect(last_response.body).to eq('required with custom works!')
|
688
723
|
end
|
689
724
|
|
690
725
|
it 'validates when param is not present' do
|
691
726
|
get '/required_custom'
|
692
|
-
last_response.status.
|
693
|
-
last_response.body.
|
727
|
+
expect(last_response.status).to eq(400)
|
728
|
+
expect(last_response.body).to eq('custom is missing, custom is not custom!')
|
694
729
|
end
|
695
730
|
|
696
731
|
context 'nested namespaces' do
|
@@ -737,33 +772,33 @@ describe Grape::Validations do
|
|
737
772
|
|
738
773
|
specify 'the parent namespace uses the validator' do
|
739
774
|
get '/nested/one', custom: 'im wrong, validate me'
|
740
|
-
last_response.status.
|
741
|
-
last_response.body.
|
775
|
+
expect(last_response.status).to eq(400)
|
776
|
+
expect(last_response.body).to eq('custom is not custom!')
|
742
777
|
end
|
743
778
|
|
744
779
|
specify 'the nested namesapce inherits the custom validator' do
|
745
780
|
get '/nested/nested/two', custom: 'im wrong, validate me'
|
746
|
-
last_response.status.
|
747
|
-
last_response.body.
|
781
|
+
expect(last_response.status).to eq(400)
|
782
|
+
expect(last_response.body).to eq('custom is not custom!')
|
748
783
|
end
|
749
784
|
|
750
785
|
specify 'peer namesapces does not have the validator' do
|
751
786
|
get '/peer/one', custom: 'im not validated'
|
752
|
-
last_response.status.
|
753
|
-
last_response.body.
|
787
|
+
expect(last_response.status).to eq(200)
|
788
|
+
expect(last_response.body).to eq('no validation required')
|
754
789
|
end
|
755
790
|
|
756
791
|
specify 'namespaces nested in peers should also not have the validator' do
|
757
792
|
get '/peer/nested/two', custom: 'im not validated'
|
758
|
-
last_response.status.
|
759
|
-
last_response.body.
|
793
|
+
expect(last_response.status).to eq(200)
|
794
|
+
expect(last_response.body).to eq('no validation required')
|
760
795
|
end
|
761
796
|
|
762
797
|
specify 'when nested, specifying a route should clear out the validations for deeper nested params' do
|
763
798
|
get '/unrelated/one'
|
764
|
-
last_response.status.
|
799
|
+
expect(last_response.status).to eq(400)
|
765
800
|
get '/unrelated/double/two'
|
766
|
-
last_response.status.
|
801
|
+
expect(last_response.status).to eq(200)
|
767
802
|
end
|
768
803
|
end
|
769
804
|
end
|
@@ -811,17 +846,58 @@ describe Grape::Validations do
|
|
811
846
|
subject.params do
|
812
847
|
use :pagination
|
813
848
|
end
|
814
|
-
subject.settings[:declared_params].
|
849
|
+
expect(subject.settings[:declared_params]).to eq [:page, :per_page]
|
815
850
|
end
|
816
851
|
|
817
852
|
it 'by #use with multiple params' do
|
818
853
|
subject.params do
|
819
854
|
use :pagination, :period
|
820
855
|
end
|
821
|
-
subject.settings[:declared_params].
|
856
|
+
expect(subject.settings[:declared_params]).to eq [:page, :per_page, :start_date, :end_date]
|
822
857
|
end
|
823
858
|
|
824
859
|
end
|
860
|
+
|
861
|
+
context 'with block' do
|
862
|
+
before do
|
863
|
+
subject.helpers do
|
864
|
+
params :order do |options|
|
865
|
+
optional :order, type: Symbol, values: [:asc, :desc], default: options[:default_order]
|
866
|
+
optional :order_by, type: Symbol, values: options[:order_by], default: options[:default_order_by]
|
867
|
+
end
|
868
|
+
end
|
869
|
+
subject.format :json
|
870
|
+
subject.params do
|
871
|
+
use :order, default_order: :asc, order_by: [:name, :created_at], default_order_by: :created_at
|
872
|
+
end
|
873
|
+
subject.get '/order' do
|
874
|
+
{
|
875
|
+
order: params[:order],
|
876
|
+
order_by: params[:order_by]
|
877
|
+
}
|
878
|
+
end
|
879
|
+
end
|
880
|
+
it 'returns defaults' do
|
881
|
+
get '/order'
|
882
|
+
expect(last_response.status).to eq(200)
|
883
|
+
expect(last_response.body).to eq({ order: :asc, order_by: :created_at }.to_json)
|
884
|
+
end
|
885
|
+
it 'overrides default value for order' do
|
886
|
+
get '/order?order=desc'
|
887
|
+
expect(last_response.status).to eq(200)
|
888
|
+
expect(last_response.body).to eq({ order: :desc, order_by: :created_at }.to_json)
|
889
|
+
end
|
890
|
+
it 'overrides default value for order_by' do
|
891
|
+
get '/order?order_by=name'
|
892
|
+
expect(last_response.status).to eq(200)
|
893
|
+
expect(last_response.body).to eq({ order: :asc, order_by: :name }.to_json)
|
894
|
+
end
|
895
|
+
it 'fails with invalid value' do
|
896
|
+
get '/order?order=invalid'
|
897
|
+
expect(last_response.status).to eq(400)
|
898
|
+
expect(last_response.body).to eq('{"error":"order does not have a valid value"}')
|
899
|
+
end
|
900
|
+
end
|
825
901
|
end
|
826
902
|
|
827
903
|
context 'documentation' do
|
@@ -834,7 +910,84 @@ describe Grape::Validations do
|
|
834
910
|
subject.get '/' do
|
835
911
|
end
|
836
912
|
|
837
|
-
subject.routes.first.route_params['first_name'][:documentation].
|
913
|
+
expect(subject.routes.first.route_params['first_name'][:documentation]).to eq(documentation)
|
914
|
+
end
|
915
|
+
end
|
916
|
+
|
917
|
+
context 'mutually exclusive' do
|
918
|
+
context 'optional params' do
|
919
|
+
it 'errors when two or more are present' do
|
920
|
+
subject.params do
|
921
|
+
optional :beer
|
922
|
+
optional :wine
|
923
|
+
optional :juice
|
924
|
+
mutually_exclusive :beer, :wine, :juice
|
925
|
+
end
|
926
|
+
subject.get '/mutually_exclusive' do
|
927
|
+
'mutually_exclusive works!'
|
928
|
+
end
|
929
|
+
|
930
|
+
get '/mutually_exclusive', beer: 'string', wine: 'anotherstring'
|
931
|
+
expect(last_response.status).to eq(400)
|
932
|
+
expect(last_response.body).to eq("[:beer, :wine] are mutually exclusive")
|
933
|
+
end
|
934
|
+
end
|
935
|
+
|
936
|
+
context 'more than one set of mutually exclusive params' do
|
937
|
+
it 'errors for all sets' do
|
938
|
+
subject.params do
|
939
|
+
optional :beer
|
940
|
+
optional :wine
|
941
|
+
mutually_exclusive :beer, :wine
|
942
|
+
optional :scotch
|
943
|
+
optional :aquavit
|
944
|
+
mutually_exclusive :scotch, :aquavit
|
945
|
+
end
|
946
|
+
subject.get '/mutually_exclusive' do
|
947
|
+
'mutually_exclusive works!'
|
948
|
+
end
|
949
|
+
|
950
|
+
get '/mutually_exclusive', beer: 'true', wine: 'true', scotch: 'true', aquavit: 'true'
|
951
|
+
expect(last_response.status).to eq(400)
|
952
|
+
expect(last_response.body).to match(/\[:beer, :wine\] are mutually exclusive/)
|
953
|
+
expect(last_response.body).to match(/\[:scotch, :aquavit\] are mutually exclusive/)
|
954
|
+
end
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
context 'exactly one of' do
|
959
|
+
context 'params' do
|
960
|
+
it 'errors when two or more are present' do
|
961
|
+
subject.params do
|
962
|
+
optional :beer
|
963
|
+
optional :wine
|
964
|
+
optional :juice
|
965
|
+
exactly_one_of :beer, :wine, :juice
|
966
|
+
end
|
967
|
+
subject.get '/exactly_one_of' do
|
968
|
+
'exactly_one_of works!'
|
969
|
+
end
|
970
|
+
|
971
|
+
get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
|
972
|
+
expect(last_response.status).to eq(400)
|
973
|
+
expect(last_response.body).to eq("[:beer, :wine] are mutually exclusive")
|
974
|
+
end
|
975
|
+
|
976
|
+
it 'errors when none is selected' do
|
977
|
+
subject.params do
|
978
|
+
optional :beer
|
979
|
+
optional :wine
|
980
|
+
optional :juice
|
981
|
+
exactly_one_of :beer, :wine, :juice
|
982
|
+
end
|
983
|
+
subject.get '/exactly_one_of' do
|
984
|
+
'exactly_one_of works!'
|
985
|
+
end
|
986
|
+
|
987
|
+
get '/exactly_one_of'
|
988
|
+
expect(last_response.status).to eq(400)
|
989
|
+
expect(last_response.body).to eq("[:beer, :wine, :juice] - exactly one parameter must be provided")
|
990
|
+
end
|
838
991
|
end
|
839
992
|
end
|
840
993
|
end
|