grape 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -4
- data/Gemfile.lock +13 -13
- data/README.md +290 -12
- data/UPGRADING.md +68 -1
- data/gemfiles/rails_3.gemfile +1 -1
- data/lib/grape.rb +8 -2
- data/lib/grape/api.rb +40 -34
- data/lib/grape/dsl/configuration.rb +2 -115
- data/lib/grape/dsl/desc.rb +101 -0
- data/lib/grape/dsl/headers.rb +16 -0
- data/lib/grape/dsl/helpers.rb +5 -9
- data/lib/grape/dsl/inside_route.rb +3 -11
- data/lib/grape/dsl/logger.rb +20 -0
- data/lib/grape/dsl/parameters.rb +12 -10
- data/lib/grape/dsl/request_response.rb +17 -4
- data/lib/grape/dsl/routing.rb +24 -7
- data/lib/grape/dsl/settings.rb +8 -2
- data/lib/grape/endpoint.rb +30 -26
- data/lib/grape/error_formatter.rb +31 -0
- data/lib/grape/error_formatter/base.rb +0 -28
- data/lib/grape/error_formatter/json.rb +13 -2
- data/lib/grape/error_formatter/txt.rb +3 -1
- data/lib/grape/error_formatter/xml.rb +3 -1
- data/lib/grape/exceptions/base.rb +11 -4
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_message_body.rb +1 -1
- data/lib/grape/exceptions/invalid_version_header.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
- data/lib/grape/exceptions/method_not_allowed.rb +10 -0
- data/lib/grape/exceptions/missing_group_type.rb +1 -1
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/missing_option.rb +1 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
- data/lib/grape/exceptions/unknown_options.rb +1 -1
- data/lib/grape/exceptions/unknown_parameter.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
- data/lib/grape/exceptions/validation.rb +2 -1
- data/lib/grape/formatter.rb +31 -0
- data/lib/grape/middleware/base.rb +28 -2
- data/lib/grape/middleware/error.rb +24 -1
- data/lib/grape/middleware/formatter.rb +4 -3
- data/lib/grape/middleware/versioner/param.rb +13 -2
- data/lib/grape/parser.rb +29 -0
- data/lib/grape/util/sendfile_response.rb +19 -0
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/params_scope.rb +39 -9
- data/lib/grape/validations/types.rb +16 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +2 -2
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
- data/lib/grape/validations/validators/base.rb +26 -0
- data/lib/grape/validations/validators/coerce.rb +16 -14
- data/lib/grape/validations/validators/default.rb +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +10 -1
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
- data/lib/grape/validations/validators/presence.rb +1 -1
- data/lib/grape/validations/validators/regexp.rb +2 -2
- data/lib/grape/validations/validators/values.rb +2 -2
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +156 -21
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +38 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +43 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +37 -0
- data/spec/grape/api_spec.rb +118 -60
- data/spec/grape/dsl/configuration_spec.rb +0 -75
- data/spec/grape/dsl/desc_spec.rb +77 -0
- data/spec/grape/dsl/headers_spec.rb +32 -0
- data/spec/grape/dsl/inside_route_spec.rb +0 -18
- data/spec/grape/dsl/logger_spec.rb +26 -0
- data/spec/grape/dsl/parameters_spec.rb +13 -7
- data/spec/grape/dsl/request_response_spec.rb +17 -3
- data/spec/grape/dsl/routing_spec.rb +8 -1
- data/spec/grape/dsl/settings_spec.rb +42 -0
- data/spec/grape/endpoint_spec.rb +60 -9
- data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
- data/spec/grape/exceptions/validation_spec.rb +7 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +44 -0
- data/spec/grape/middleware/base_spec.rb +100 -0
- data/spec/grape/middleware/exception_spec.rb +1 -2
- data/spec/grape/middleware/formatter_spec.rb +12 -2
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +1 -1
- data/spec/grape/middleware/versioner/header_spec.rb +11 -1
- data/spec/grape/middleware/versioner/param_spec.rb +105 -1
- data/spec/grape/validations/params_scope_spec.rb +77 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +277 -0
- data/spec/grape/validations/validators/coerce_spec.rb +91 -0
- data/spec/grape/validations/validators/default_spec.rb +6 -0
- data/spec/grape/validations/validators/presence_spec.rb +27 -0
- data/spec/grape/validations/validators/regexp_spec.rb +36 -0
- data/spec/grape/validations/validators/values_spec.rb +44 -0
- data/spec/grape/validations_spec.rb +149 -4
- data/spec/spec_helper.rb +1 -0
- metadata +26 -5
- data/lib/grape/formatter/base.rb +0 -31
- data/lib/grape/parser/base.rb +0 -29
- data/pkg/grape-0.13.0.gem +0 -0
@@ -93,6 +93,7 @@ describe Grape::Validations::ParamsScope do
|
|
93
93
|
module ParamsScopeSpec
|
94
94
|
class CustomType
|
95
95
|
attr_reader :value
|
96
|
+
|
96
97
|
def self.parse(value)
|
97
98
|
fail if value == 'invalid'
|
98
99
|
new(value)
|
@@ -173,6 +174,24 @@ describe Grape::Validations::ParamsScope do
|
|
173
174
|
end
|
174
175
|
end
|
175
176
|
|
177
|
+
context 'when the default is an array' do
|
178
|
+
context 'and is the entire range of allowed values' do
|
179
|
+
it 'does not raise an exception' do
|
180
|
+
expect do
|
181
|
+
subject.params { optional :numbers, type: Array[Integer], values: 0..2, default: 0..2 }
|
182
|
+
end.to_not raise_error
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
context 'and is a subset of allowed values' do
|
187
|
+
it 'does not raise an exception' do
|
188
|
+
expect do
|
189
|
+
subject.params { optional :numbers, type: Array[Integer], values: [0, 1, 2], default: [1, 0] }
|
190
|
+
end.to_not raise_error
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
176
195
|
context 'when both range endpoints are #kind_of? the type' do
|
177
196
|
it 'accepts values in the range' do
|
178
197
|
subject.params do
|
@@ -297,6 +316,32 @@ describe Grape::Validations::ParamsScope do
|
|
297
316
|
expect(last_response.status).to eq(200)
|
298
317
|
end
|
299
318
|
|
319
|
+
it 'applies the validations of multiple parameters' do
|
320
|
+
subject.params do
|
321
|
+
optional :a, :b
|
322
|
+
given :a, :b do
|
323
|
+
requires :c
|
324
|
+
end
|
325
|
+
end
|
326
|
+
subject.get('/multiple') { declared(params).to_json }
|
327
|
+
|
328
|
+
get '/multiple'
|
329
|
+
expect(last_response.status).to eq(200)
|
330
|
+
|
331
|
+
get '/multiple', a: true
|
332
|
+
expect(last_response.status).to eq(200)
|
333
|
+
|
334
|
+
get '/multiple', b: true
|
335
|
+
expect(last_response.status).to eq(200)
|
336
|
+
|
337
|
+
get '/multiple', a: true, b: true
|
338
|
+
expect(last_response.status).to eq(400)
|
339
|
+
expect(last_response.body).to eq('c is missing')
|
340
|
+
|
341
|
+
get '/multiple', a: true, b: true, c: true
|
342
|
+
expect(last_response.status).to eq(200)
|
343
|
+
end
|
344
|
+
|
300
345
|
it 'raises an error if the dependent parameter was never specified' do
|
301
346
|
expect do
|
302
347
|
subject.params do
|
@@ -327,5 +372,37 @@ describe Grape::Validations::ParamsScope do
|
|
327
372
|
expect(last_response.status).to eq(400)
|
328
373
|
expect(last_response.body).to eq('bar[b] is missing')
|
329
374
|
end
|
375
|
+
|
376
|
+
it 'includes the nested parameter within #declared(params)' do
|
377
|
+
subject.params do
|
378
|
+
requires :bar, type: Hash do
|
379
|
+
optional :a
|
380
|
+
given :a do
|
381
|
+
requires :b
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
subject.get('/nested') { declared(params).to_json }
|
386
|
+
|
387
|
+
get '/nested', bar: { a: true, b: 'yes' }
|
388
|
+
expect(JSON.parse(last_response.body)).to eq('bar' => { 'a' => 'true', 'b' => 'yes' })
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'includes level 2 nested parameters outside the given within #declared(params)' do
|
392
|
+
subject.params do
|
393
|
+
requires :bar, type: Hash do
|
394
|
+
optional :a
|
395
|
+
given :a do
|
396
|
+
requires :c, type: Hash do
|
397
|
+
requires :b
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
subject.get('/nested') { declared(params).to_json }
|
403
|
+
|
404
|
+
get '/nested', bar: { a: true, c: { b: 'yes' } }
|
405
|
+
expect(JSON.parse(last_response.body)).to eq('bar' => { 'a' => 'true', 'c' => { 'b' => 'yes' } })
|
406
|
+
end
|
330
407
|
end
|
331
408
|
end
|
@@ -120,6 +120,123 @@ describe Grape::Validations::AllowBlankValidator do
|
|
120
120
|
end
|
121
121
|
end
|
122
122
|
get '/disallow_string_value_in_an_optional_hash_group'
|
123
|
+
|
124
|
+
resources :custom_message do
|
125
|
+
params do
|
126
|
+
requires :name, allow_blank: { value: false, message: 'has no value' }
|
127
|
+
end
|
128
|
+
get
|
129
|
+
|
130
|
+
params do
|
131
|
+
optional :name, allow_blank: { value: false, message: 'has no value' }
|
132
|
+
end
|
133
|
+
get '/disallow_blank_optional_param'
|
134
|
+
|
135
|
+
params do
|
136
|
+
requires :name, allow_blank: true
|
137
|
+
end
|
138
|
+
get '/allow_blank'
|
139
|
+
|
140
|
+
params do
|
141
|
+
requires :val, type: DateTime, allow_blank: true
|
142
|
+
end
|
143
|
+
get '/allow_datetime_blank'
|
144
|
+
|
145
|
+
params do
|
146
|
+
requires :val, type: DateTime, allow_blank: { value: false, message: 'has no value' }
|
147
|
+
end
|
148
|
+
get '/disallow_datetime_blank'
|
149
|
+
|
150
|
+
params do
|
151
|
+
requires :val, type: DateTime
|
152
|
+
end
|
153
|
+
get '/default_allow_datetime_blank'
|
154
|
+
|
155
|
+
params do
|
156
|
+
requires :val, type: Date, allow_blank: true
|
157
|
+
end
|
158
|
+
get '/allow_date_blank'
|
159
|
+
|
160
|
+
params do
|
161
|
+
requires :val, type: Integer, allow_blank: true
|
162
|
+
end
|
163
|
+
get '/allow_integer_blank'
|
164
|
+
|
165
|
+
params do
|
166
|
+
requires :val, type: Float, allow_blank: true
|
167
|
+
end
|
168
|
+
get '/allow_float_blank'
|
169
|
+
|
170
|
+
params do
|
171
|
+
requires :val, type: Fixnum, allow_blank: true
|
172
|
+
end
|
173
|
+
get '/allow_fixnum_blank'
|
174
|
+
|
175
|
+
params do
|
176
|
+
requires :val, type: Symbol, allow_blank: true
|
177
|
+
end
|
178
|
+
get '/allow_symbol_blank'
|
179
|
+
|
180
|
+
params do
|
181
|
+
requires :val, type: Boolean, allow_blank: true
|
182
|
+
end
|
183
|
+
get '/allow_boolean_blank'
|
184
|
+
|
185
|
+
params do
|
186
|
+
requires :val, type: Boolean, allow_blank: { value: false, message: 'has no value' }
|
187
|
+
end
|
188
|
+
get '/disallow_boolean_blank'
|
189
|
+
|
190
|
+
params do
|
191
|
+
optional :user, type: Hash do
|
192
|
+
requires :name, allow_blank: { value: false, message: 'has no value' }
|
193
|
+
end
|
194
|
+
end
|
195
|
+
get '/disallow_blank_required_param_in_an_optional_group'
|
196
|
+
|
197
|
+
params do
|
198
|
+
optional :user, type: Hash do
|
199
|
+
requires :name, type: Date, allow_blank: true
|
200
|
+
end
|
201
|
+
end
|
202
|
+
get '/allow_blank_date_param_in_an_optional_group'
|
203
|
+
|
204
|
+
params do
|
205
|
+
optional :user, type: Hash do
|
206
|
+
optional :name, allow_blank: { value: false, message: 'has no value' }
|
207
|
+
requires :age
|
208
|
+
end
|
209
|
+
end
|
210
|
+
get '/disallow_blank_optional_param_in_an_optional_group'
|
211
|
+
|
212
|
+
params do
|
213
|
+
requires :user, type: Hash do
|
214
|
+
requires :name, allow_blank: { value: false, message: 'has no value' }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
get '/disallow_blank_required_param_in_a_required_group'
|
218
|
+
|
219
|
+
params do
|
220
|
+
requires :user, type: Hash do
|
221
|
+
requires :name, allow_blank: { value: false, message: 'has no value' }
|
222
|
+
end
|
223
|
+
end
|
224
|
+
get '/disallow_string_value_in_a_required_hash_group'
|
225
|
+
|
226
|
+
params do
|
227
|
+
requires :user, type: Hash do
|
228
|
+
optional :name, allow_blank: { value: false, message: 'has no value' }
|
229
|
+
end
|
230
|
+
end
|
231
|
+
get '/disallow_blank_optional_param_in_a_required_group'
|
232
|
+
|
233
|
+
params do
|
234
|
+
optional :user, type: Hash do
|
235
|
+
optional :name, allow_blank: { value: false, message: 'has no value' }
|
236
|
+
end
|
237
|
+
end
|
238
|
+
get '/disallow_string_value_in_an_optional_hash_group'
|
239
|
+
end
|
123
240
|
end
|
124
241
|
end
|
125
242
|
end
|
@@ -154,6 +271,166 @@ describe Grape::Validations::AllowBlankValidator do
|
|
154
271
|
end
|
155
272
|
end
|
156
273
|
|
274
|
+
context 'custom validation message' do
|
275
|
+
context 'with invalid input' do
|
276
|
+
it 'refuses empty string' do
|
277
|
+
get '/custom_message', name: ''
|
278
|
+
expect(last_response.body).to eq('{"error":"name has no value"}')
|
279
|
+
end
|
280
|
+
it 'refuses empty string for an optional param' do
|
281
|
+
get '/custom_message/disallow_blank_optional_param', name: ''
|
282
|
+
expect(last_response.body).to eq('{"error":"name has no value"}')
|
283
|
+
end
|
284
|
+
it 'refuses only whitespaces' do
|
285
|
+
get '/custom_message', name: ' '
|
286
|
+
expect(last_response.body).to eq('{"error":"name has no value"}')
|
287
|
+
|
288
|
+
get '/custom_message', name: " \n "
|
289
|
+
expect(last_response.body).to eq('{"error":"name has no value"}')
|
290
|
+
|
291
|
+
get '/custom_message', name: "\n"
|
292
|
+
expect(last_response.body).to eq('{"error":"name has no value"}')
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'refuses nil' do
|
296
|
+
get '/custom_message', name: nil
|
297
|
+
expect(last_response.body).to eq('{"error":"name has no value"}')
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
context 'with valid input' do
|
302
|
+
it 'accepts valid input' do
|
303
|
+
get '/custom_message', name: 'bob'
|
304
|
+
expect(last_response.status).to eq(200)
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'accepts empty input when allow_blank is false' do
|
308
|
+
get '/custom_message/allow_blank', name: ''
|
309
|
+
expect(last_response.status).to eq(200)
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'accepts empty input' do
|
313
|
+
get '/custom_message/default_allow_datetime_blank', val: ''
|
314
|
+
expect(last_response.status).to eq(200)
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'accepts empty when datetime allow_blank' do
|
318
|
+
get '/custom_message/allow_datetime_blank', val: ''
|
319
|
+
expect(last_response.status).to eq(200)
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'accepts empty when date allow_blank' do
|
323
|
+
get '/custom_message/allow_date_blank', val: ''
|
324
|
+
expect(last_response.status).to eq(200)
|
325
|
+
end
|
326
|
+
|
327
|
+
context 'allow_blank when Numeric' do
|
328
|
+
it 'accepts empty when integer allow_blank' do
|
329
|
+
get '/custom_message/allow_integer_blank', val: ''
|
330
|
+
expect(last_response.status).to eq(200)
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'accepts empty when float allow_blank' do
|
334
|
+
get '/custom_message/allow_float_blank', val: ''
|
335
|
+
expect(last_response.status).to eq(200)
|
336
|
+
end
|
337
|
+
|
338
|
+
it 'accepts empty when fixnum allow_blank' do
|
339
|
+
get '/custom_message/allow_fixnum_blank', val: ''
|
340
|
+
expect(last_response.status).to eq(200)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'accepts empty when symbol allow_blank' do
|
345
|
+
get '/custom_message/allow_symbol_blank', val: ''
|
346
|
+
expect(last_response.status).to eq(200)
|
347
|
+
end
|
348
|
+
|
349
|
+
it 'accepts empty when boolean allow_blank' do
|
350
|
+
get '/custom_message/allow_boolean_blank', val: ''
|
351
|
+
expect(last_response.status).to eq(200)
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'accepts false when boolean allow_blank' do
|
355
|
+
get '/custom_message/disallow_boolean_blank', val: false
|
356
|
+
expect(last_response.status).to eq(200)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
context 'in an optional group' do
|
361
|
+
context 'as a required param' do
|
362
|
+
it 'accepts a missing group, even with a disallwed blank param' do
|
363
|
+
get '/custom_message/disallow_blank_required_param_in_an_optional_group'
|
364
|
+
expect(last_response.status).to eq(200)
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'accepts a nested missing date value' do
|
368
|
+
get '/custom_message/allow_blank_date_param_in_an_optional_group', user: { name: '' }
|
369
|
+
expect(last_response.status).to eq(200)
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'refuses a blank value in an existing group' do
|
373
|
+
get '/custom_message/disallow_blank_required_param_in_an_optional_group', user: { name: '' }
|
374
|
+
expect(last_response.status).to eq(400)
|
375
|
+
expect(last_response.body).to eq('{"error":"user[name] has no value"}')
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
context 'as an optional param' do
|
380
|
+
it 'accepts a missing group, even with a disallwed blank param' do
|
381
|
+
get '/custom_message/disallow_blank_optional_param_in_an_optional_group'
|
382
|
+
expect(last_response.status).to eq(200)
|
383
|
+
end
|
384
|
+
|
385
|
+
it 'accepts a nested missing optional value' do
|
386
|
+
get '/custom_message/disallow_blank_optional_param_in_an_optional_group', user: { age: '29' }
|
387
|
+
expect(last_response.status).to eq(200)
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'refuses a blank existing value in an existing scope' do
|
391
|
+
get '/custom_message/disallow_blank_optional_param_in_an_optional_group', user: { age: '29', name: '' }
|
392
|
+
expect(last_response.status).to eq(400)
|
393
|
+
expect(last_response.body).to eq('{"error":"user[name] has no value"}')
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
context 'in a required group' do
|
399
|
+
context 'as a required param' do
|
400
|
+
it 'refuses a blank value in a required existing group' do
|
401
|
+
get '/custom_message/disallow_blank_required_param_in_a_required_group', user: { name: '' }
|
402
|
+
expect(last_response.status).to eq(400)
|
403
|
+
expect(last_response.body).to eq('{"error":"user[name] has no value"}')
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'refuses a string value in a required hash group' do
|
407
|
+
get '/custom_message/disallow_string_value_in_a_required_hash_group', user: ''
|
408
|
+
expect(last_response.status).to eq(400)
|
409
|
+
expect(last_response.body).to eq('{"error":"user is invalid, user[name] is missing"}')
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
context 'as an optional param' do
|
414
|
+
it 'accepts a nested missing value' do
|
415
|
+
get '/custom_message/disallow_blank_optional_param_in_a_required_group', user: { age: '29' }
|
416
|
+
expect(last_response.status).to eq(200)
|
417
|
+
end
|
418
|
+
|
419
|
+
it 'refuses a blank existing value in an existing scope' do
|
420
|
+
get '/custom_message/disallow_blank_optional_param_in_a_required_group', user: { age: '29', name: '' }
|
421
|
+
expect(last_response.status).to eq(400)
|
422
|
+
expect(last_response.body).to eq('{"error":"user[name] has no value"}')
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'refuses a string value in an optional hash group' do
|
426
|
+
get '/custom_message/disallow_string_value_in_an_optional_hash_group', user: ''
|
427
|
+
expect(last_response.status).to eq(400)
|
428
|
+
expect(last_response.body).to eq('{"error":"user is invalid"}')
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
157
434
|
context 'valid input' do
|
158
435
|
it 'accepts valid input' do
|
159
436
|
get '/', name: 'bob'
|
@@ -55,6 +55,62 @@ describe Grape::Validations::CoerceValidator do
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
context 'with a custom validation message' do
|
59
|
+
it 'errors on malformed input' do
|
60
|
+
subject.params do
|
61
|
+
requires :int, type: { value: Integer, message: 'type cast is invalid' }
|
62
|
+
end
|
63
|
+
subject.get '/single' do
|
64
|
+
'int works'
|
65
|
+
end
|
66
|
+
|
67
|
+
get '/single', int: '43a'
|
68
|
+
expect(last_response.status).to eq(400)
|
69
|
+
expect(last_response.body).to eq('int type cast is invalid')
|
70
|
+
|
71
|
+
get '/single', int: '43'
|
72
|
+
expect(last_response.status).to eq(200)
|
73
|
+
expect(last_response.body).to eq('int works')
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'on custom coercion rules' do
|
77
|
+
before do
|
78
|
+
subject.params do
|
79
|
+
requires :a, types: { value: [Boolean, String], message: 'type cast is invalid' }, coerce_with: (lambda do |val|
|
80
|
+
if val == 'yup'
|
81
|
+
true
|
82
|
+
elsif val == 'false'
|
83
|
+
0
|
84
|
+
else
|
85
|
+
val
|
86
|
+
end
|
87
|
+
end)
|
88
|
+
end
|
89
|
+
subject.get '/' do
|
90
|
+
params[:a].class.to_s
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'respects :coerce_with' do
|
95
|
+
get '/', a: 'yup'
|
96
|
+
expect(last_response.status).to eq(200)
|
97
|
+
expect(last_response.body).to eq('TrueClass')
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'still validates type' do
|
101
|
+
get '/', a: 'false'
|
102
|
+
expect(last_response.status).to eq(400)
|
103
|
+
expect(last_response.body).to eq('a type cast is invalid')
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'performs no additional coercion' do
|
107
|
+
get '/', a: 'true'
|
108
|
+
expect(last_response.status).to eq(200)
|
109
|
+
expect(last_response.body).to eq('String')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
58
114
|
it 'error on malformed input' do
|
59
115
|
subject.params do
|
60
116
|
requires :int, type: Integer
|
@@ -368,6 +424,41 @@ describe Grape::Validations::CoerceValidator do
|
|
368
424
|
expect(last_response.body).to eq('splines[x] does not have a valid value')
|
369
425
|
end
|
370
426
|
|
427
|
+
it 'works when declared optional' do
|
428
|
+
subject.params do
|
429
|
+
optional :splines, type: JSON do
|
430
|
+
requires :x, type: Integer, values: [1, 2, 3]
|
431
|
+
optional :ints, type: Array[Integer]
|
432
|
+
optional :obj, type: Hash do
|
433
|
+
optional :y
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
subject.get '/' do
|
438
|
+
if params[:splines].is_a? Hash
|
439
|
+
params[:splines][:obj][:y]
|
440
|
+
else
|
441
|
+
'arrays work' if params[:splines].any? { |s| s.key? :obj }
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
get '/', splines: '{"x":1,"ints":[1,2,3],"obj":{"y":"woof"}}'
|
446
|
+
expect(last_response.status).to eq(200)
|
447
|
+
expect(last_response.body).to eq('woof')
|
448
|
+
|
449
|
+
get '/', splines: '[{"x":2,"ints":[]},{"x":3,"ints":[4],"obj":{"y":"quack"}}]'
|
450
|
+
expect(last_response.status).to eq(200)
|
451
|
+
expect(last_response.body).to eq('arrays work')
|
452
|
+
|
453
|
+
get '/', splines: '{"x":4,"ints":[2]}'
|
454
|
+
expect(last_response.status).to eq(400)
|
455
|
+
expect(last_response.body).to eq('splines[x] does not have a valid value')
|
456
|
+
|
457
|
+
get '/', splines: '[{"x":1,"ints":[]},{"x":4,"ints":[]}]'
|
458
|
+
expect(last_response.status).to eq(400)
|
459
|
+
expect(last_response.body).to eq('splines[x] does not have a valid value')
|
460
|
+
end
|
461
|
+
|
371
462
|
it 'accepts Array[JSON] shorthand' do
|
372
463
|
subject.params do
|
373
464
|
requires :splines, type: Array[JSON] do
|