grape 1.2.5 → 1.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -0
- data/LICENSE +1 -1
- data/README.md +53 -16
- data/UPGRADING.md +231 -23
- data/grape.gemspec +10 -1
- data/lib/grape.rb +6 -7
- data/lib/grape/api.rb +4 -2
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +36 -33
- data/lib/grape/config.rb +2 -0
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/api.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +2 -0
- data/lib/grape/dsl/configuration.rb +2 -0
- data/lib/grape/dsl/desc.rb +2 -0
- data/lib/grape/dsl/headers.rb +2 -0
- data/lib/grape/dsl/helpers.rb +4 -2
- data/lib/grape/dsl/inside_route.rb +83 -34
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +2 -0
- data/lib/grape/dsl/parameters.rb +8 -6
- data/lib/grape/dsl/request_response.rb +4 -2
- data/lib/grape/dsl/routing.rb +9 -5
- data/lib/grape/dsl/settings.rb +7 -1
- data/lib/grape/dsl/validations.rb +20 -1
- data/lib/grape/eager_load.rb +3 -1
- data/lib/grape/endpoint.rb +21 -13
- data/lib/grape/error_formatter.rb +3 -1
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/error_formatter/json.rb +2 -0
- data/lib/grape/error_formatter/txt.rb +2 -0
- data/lib/grape/error_formatter/xml.rb +2 -0
- data/lib/grape/exceptions/base.rb +11 -13
- data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
- data/lib/grape/exceptions/invalid_formatter.rb +2 -0
- data/lib/grape/exceptions/invalid_message_body.rb +2 -0
- data/lib/grape/exceptions/invalid_response.rb +2 -0
- data/lib/grape/exceptions/invalid_version_header.rb +2 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
- data/lib/grape/exceptions/method_not_allowed.rb +2 -0
- data/lib/grape/exceptions/missing_group_type.rb +2 -0
- data/lib/grape/exceptions/missing_mime_type.rb +2 -0
- data/lib/grape/exceptions/missing_option.rb +2 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
- data/lib/grape/exceptions/unknown_options.rb +2 -0
- data/lib/grape/exceptions/unknown_parameter.rb +2 -0
- data/lib/grape/exceptions/unknown_validator.rb +2 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
- data/lib/grape/exceptions/validation.rb +3 -1
- data/lib/grape/exceptions/validation_array_errors.rb +2 -0
- data/lib/grape/exceptions/validation_errors.rb +13 -12
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
- data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
- data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
- data/lib/grape/extensions/hash.rb +2 -0
- data/lib/grape/extensions/hashie/mash.rb +2 -0
- data/lib/grape/formatter.rb +5 -3
- data/lib/grape/formatter/json.rb +2 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -0
- data/lib/grape/formatter/txt.rb +2 -0
- data/lib/grape/formatter/xml.rb +2 -0
- data/lib/grape/http/headers.rb +50 -18
- data/lib/grape/middleware/auth/base.rb +2 -0
- data/lib/grape/middleware/auth/dsl.rb +2 -0
- data/lib/grape/middleware/auth/strategies.rb +2 -0
- data/lib/grape/middleware/auth/strategy_info.rb +2 -0
- data/lib/grape/middleware/base.rb +7 -7
- data/lib/grape/middleware/error.rb +3 -1
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +8 -6
- data/lib/grape/middleware/globals.rb +2 -0
- data/lib/grape/middleware/helpers.rb +2 -0
- data/lib/grape/middleware/stack.rb +4 -1
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +3 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
- data/lib/grape/middleware/versioner/path.rb +3 -1
- data/lib/grape/namespace.rb +14 -2
- data/lib/grape/parser.rb +3 -1
- data/lib/grape/parser/json.rb +2 -0
- data/lib/grape/parser/xml.rb +2 -0
- data/lib/grape/path.rb +15 -3
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +15 -8
- data/lib/grape/router.rb +30 -29
- data/lib/grape/router/attribute_translator.rb +39 -8
- data/lib/grape/router/pattern.rb +20 -16
- data/lib/grape/router/route.rb +12 -26
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
- data/lib/grape/util/base_inheritable.rb +15 -6
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/endpoint_configuration.rb +2 -0
- data/lib/grape/util/env.rb +19 -17
- data/lib/grape/util/inheritable_setting.rb +2 -0
- data/lib/grape/util/inheritable_values.rb +2 -0
- data/lib/grape/util/json.rb +2 -0
- data/lib/grape/util/lazy_block.rb +2 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +2 -0
- data/lib/grape/util/registrable.rb +2 -0
- data/lib/grape/util/reverse_stackable_values.rb +4 -0
- data/lib/grape/util/stackable_values.rb +10 -20
- data/lib/grape/util/strict_hash_configuration.rb +2 -0
- data/lib/grape/util/xml.rb +2 -0
- data/lib/grape/validations.rb +2 -0
- data/lib/grape/validations/attributes_iterator.rb +3 -3
- data/lib/grape/validations/multiple_attributes_iterator.rb +2 -0
- data/lib/grape/validations/params_scope.rb +27 -14
- data/lib/grape/validations/single_attribute_iterator.rb +13 -2
- data/lib/grape/validations/types.rb +12 -34
- data/lib/grape/validations/types/array_coercer.rb +65 -0
- data/lib/grape/validations/types/build_coercer.rb +47 -49
- data/lib/grape/validations/types/custom_type_coercer.rb +15 -49
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
- data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
- data/lib/grape/validations/types/file.rb +22 -18
- data/lib/grape/validations/types/json.rb +46 -39
- data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
- data/lib/grape/validations/types/primitive_coercer.rb +67 -0
- data/lib/grape/validations/types/set_coercer.rb +40 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
- data/lib/grape/validations/validator_factory.rb +2 -0
- data/lib/grape/validations/validators/all_or_none.rb +3 -1
- data/lib/grape/validations/validators/allow_blank.rb +3 -1
- data/lib/grape/validations/validators/as.rb +2 -0
- data/lib/grape/validations/validators/at_least_one_of.rb +3 -1
- data/lib/grape/validations/validators/base.rb +8 -5
- data/lib/grape/validations/validators/coerce.rb +39 -29
- data/lib/grape/validations/validators/default.rb +2 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +6 -2
- data/lib/grape/validations/validators/except_values.rb +3 -1
- data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
- data/lib/grape/validations/validators/mutual_exclusion.rb +3 -1
- data/lib/grape/validations/validators/presence.rb +3 -1
- data/lib/grape/validations/validators/regexp.rb +4 -2
- data/lib/grape/validations/validators/same_as.rb +6 -3
- data/lib/grape/validations/validators/values.rb +17 -5
- data/lib/grape/version.rb +3 -1
- data/spec/grape/api/custom_validations_spec.rb +5 -3
- data/spec/grape/api/deeply_included_options_spec.rb +2 -0
- data/spec/grape/api/defines_boolean_in_params_spec.rb +5 -3
- data/spec/grape/api/inherited_helpers_spec.rb +2 -0
- data/spec/grape/api/instance_spec.rb +104 -0
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/nested_helpers_spec.rb +2 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/parameters_modification_spec.rb +3 -1
- data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +2 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +2 -0
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
- data/spec/grape/api/shared_helpers_spec.rb +2 -0
- data/spec/grape/api_remount_spec.rb +2 -0
- data/spec/grape/api_spec.rb +99 -11
- data/spec/grape/config_spec.rb +2 -0
- data/spec/grape/dsl/callbacks_spec.rb +2 -0
- data/spec/grape/dsl/configuration_spec.rb +2 -0
- data/spec/grape/dsl/desc_spec.rb +2 -0
- data/spec/grape/dsl/headers_spec.rb +2 -0
- data/spec/grape/dsl/helpers_spec.rb +4 -2
- data/spec/grape/dsl/inside_route_spec.rb +177 -33
- data/spec/grape/dsl/logger_spec.rb +2 -0
- data/spec/grape/dsl/middleware_spec.rb +2 -0
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +2 -0
- data/spec/grape/dsl/routing_spec.rb +2 -0
- data/spec/grape/dsl/settings_spec.rb +2 -0
- data/spec/grape/dsl/validations_spec.rb +2 -0
- data/spec/grape/endpoint_spec.rb +21 -6
- data/spec/grape/entity_spec.rb +2 -0
- data/spec/grape/exceptions/base_spec.rb +3 -1
- data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_response_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
- data/spec/grape/exceptions/missing_option_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +4 -2
- data/spec/grape/exceptions/validation_spec.rb +3 -1
- data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
- data/spec/grape/integration/rack_spec.rb +3 -1
- data/spec/grape/loading_spec.rb +2 -0
- data/spec/grape/middleware/auth/base_spec.rb +2 -0
- data/spec/grape/middleware/auth/dsl_spec.rb +2 -0
- data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
- data/spec/grape/middleware/base_spec.rb +2 -0
- data/spec/grape/middleware/error_spec.rb +2 -0
- data/spec/grape/middleware/exception_spec.rb +3 -1
- data/spec/grape/middleware/formatter_spec.rb +19 -12
- data/spec/grape/middleware/globals_spec.rb +2 -0
- data/spec/grape/middleware/stack_spec.rb +11 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
- data/spec/grape/middleware/versioner/header_spec.rb +3 -1
- data/spec/grape/middleware/versioner/param_spec.rb +3 -1
- data/spec/grape/middleware/versioner/path_spec.rb +3 -1
- data/spec/grape/middleware/versioner_spec.rb +2 -0
- data/spec/grape/named_api_spec.rb +2 -0
- data/spec/grape/parser_spec.rb +7 -5
- data/spec/grape/path_spec.rb +6 -4
- data/spec/grape/presenters/presenter_spec.rb +2 -0
- data/spec/grape/request_spec.rb +2 -0
- data/spec/grape/util/inheritable_setting_spec.rb +2 -0
- data/spec/grape/util/inheritable_values_spec.rb +2 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
- data/spec/grape/util/stackable_values_spec.rb +3 -1
- data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
- data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +2 -0
- data/spec/grape/validations/params_scope_spec.rb +3 -1
- data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -4
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +9 -36
- data/spec/grape/validations/validators/all_or_none_spec.rb +2 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +2 -0
- data/spec/grape/validations/validators/coerce_spec.rb +341 -136
- data/spec/grape/validations/validators/default_spec.rb +123 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +14 -12
- data/spec/grape/validations/validators/except_values_spec.rb +3 -1
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +2 -0
- data/spec/grape/validations/validators/presence_spec.rb +30 -0
- data/spec/grape/validations/validators/regexp_spec.rb +2 -0
- data/spec/grape/validations/validators/same_as_spec.rb +2 -0
- data/spec/grape/validations/validators/values_spec.rb +30 -5
- data/spec/grape/validations_spec.rb +91 -33
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +2 -0
- data/spec/integration/multi_xml/xml_spec.rb +2 -0
- data/spec/shared/versioning_examples.rb +2 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/basic_auth_encode_helpers.rb +2 -0
- data/spec/support/content_type_helpers.rb +2 -0
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/endpoint_faker.rb +2 -0
- data/spec/support/file_streamer.rb +2 -0
- data/spec/support/integer_helpers.rb +2 -0
- data/spec/support/versioned_helpers.rb +4 -2
- metadata +48 -28
- data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
- data/lib/grape/util/content_types.rb +0 -26
- data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Grape::Validations::DefaultValidator do
|
@@ -296,4 +298,125 @@ describe Grape::Validations::DefaultValidator do
|
|
296
298
|
end
|
297
299
|
end
|
298
300
|
end
|
301
|
+
|
302
|
+
context 'optional with nil as value' do
|
303
|
+
subject do
|
304
|
+
Class.new(Grape::API) do
|
305
|
+
default_format :json
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def app
|
310
|
+
subject
|
311
|
+
end
|
312
|
+
|
313
|
+
context 'primitive types' do
|
314
|
+
[
|
315
|
+
[Integer, 0],
|
316
|
+
[Integer, 42],
|
317
|
+
[Float, 0.0],
|
318
|
+
[Float, 4.2],
|
319
|
+
[BigDecimal, 0.0],
|
320
|
+
[BigDecimal, 4.2],
|
321
|
+
[Numeric, 0],
|
322
|
+
[Numeric, 42],
|
323
|
+
[Date, Date.today],
|
324
|
+
[DateTime, DateTime.now],
|
325
|
+
[Time, Time.now],
|
326
|
+
[Time, Time.at(0)],
|
327
|
+
[Grape::API::Boolean, false],
|
328
|
+
[String, ''],
|
329
|
+
[String, 'non-empty-string'],
|
330
|
+
[Symbol, :symbol],
|
331
|
+
[TrueClass, true],
|
332
|
+
[FalseClass, false]
|
333
|
+
].each do |type, default|
|
334
|
+
it 'respects the default value' do
|
335
|
+
subject.params do
|
336
|
+
optional :param, type: type, default: default
|
337
|
+
end
|
338
|
+
subject.get '/default_value' do
|
339
|
+
params[:param]
|
340
|
+
end
|
341
|
+
|
342
|
+
get '/default_value', param: nil
|
343
|
+
expect(last_response.status).to eq(200)
|
344
|
+
expect(last_response.body).to eq(default.to_json)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
context 'structures types' do
|
350
|
+
[
|
351
|
+
[Hash, {}],
|
352
|
+
[Hash, { test: 'non-empty' }],
|
353
|
+
[Array, []],
|
354
|
+
[Array, ['non-empty']],
|
355
|
+
[Array[Integer], []],
|
356
|
+
[Set, []],
|
357
|
+
[Set, [1]]
|
358
|
+
].each do |type, default|
|
359
|
+
it 'respects the default value' do
|
360
|
+
subject.params do
|
361
|
+
optional :param, type: type, default: default
|
362
|
+
end
|
363
|
+
subject.get '/default_value' do
|
364
|
+
params[:param]
|
365
|
+
end
|
366
|
+
|
367
|
+
get '/default_value', param: nil
|
368
|
+
expect(last_response.status).to eq(200)
|
369
|
+
expect(last_response.body).to eq(default.to_json)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
context 'special types' do
|
375
|
+
[
|
376
|
+
[JSON, ''],
|
377
|
+
[JSON, { test: 'non-empty-string' }.to_json],
|
378
|
+
[Array[JSON], []],
|
379
|
+
[Array[JSON], [{ test: 'non-empty-string' }.to_json]],
|
380
|
+
[::File, ''],
|
381
|
+
[::File, { test: 'non-empty-string' }.to_json],
|
382
|
+
[Rack::Multipart::UploadedFile, ''],
|
383
|
+
[Rack::Multipart::UploadedFile, { test: 'non-empty-string' }.to_json]
|
384
|
+
].each do |type, default|
|
385
|
+
it 'respects the default value' do
|
386
|
+
subject.params do
|
387
|
+
optional :param, type: type, default: default
|
388
|
+
end
|
389
|
+
subject.get '/default_value' do
|
390
|
+
params[:param]
|
391
|
+
end
|
392
|
+
|
393
|
+
get '/default_value', param: nil
|
394
|
+
expect(last_response.status).to eq(200)
|
395
|
+
expect(last_response.body).to eq(default.to_json)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context 'variant-member-type collections' do
|
401
|
+
[
|
402
|
+
[Array[Integer, String], [0, '']],
|
403
|
+
[Array[Integer, String], [42, 'non-empty-string']],
|
404
|
+
[[Integer, String, Array[Integer, String]], [0, '', [0, '']]],
|
405
|
+
[[Integer, String, Array[Integer, String]], [42, 'non-empty-string', [42, 'non-empty-string']]]
|
406
|
+
].each do |type, default|
|
407
|
+
it 'respects the default value' do
|
408
|
+
subject.params do
|
409
|
+
optional :param, type: type, default: default
|
410
|
+
end
|
411
|
+
subject.get '/default_value' do
|
412
|
+
params[:param]
|
413
|
+
end
|
414
|
+
|
415
|
+
get '/default_value', param: nil
|
416
|
+
expect(last_response.status).to eq(200)
|
417
|
+
expect(last_response.body).to eq(default.to_json)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
299
422
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Grape::Validations::ExactlyOneOfValidator do
|
@@ -98,7 +100,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
|
|
98
100
|
validate
|
99
101
|
expect(last_response.status).to eq 400
|
100
102
|
expect(JSON.parse(last_response.body)).to eq(
|
101
|
-
'beer,wine,grapefruit' => ['are
|
103
|
+
'beer,wine,grapefruit' => ['are mutually exclusive']
|
102
104
|
)
|
103
105
|
end
|
104
106
|
|
@@ -110,7 +112,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
|
|
110
112
|
validate
|
111
113
|
expect(last_response.status).to eq 400
|
112
114
|
expect(JSON.parse(last_response.body)).to eq(
|
113
|
-
'beer,wine,grapefruit' => ['are
|
115
|
+
'beer,wine,grapefruit' => ['are mutually exclusive']
|
114
116
|
)
|
115
117
|
end
|
116
118
|
end
|
@@ -124,7 +126,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
|
|
124
126
|
validate
|
125
127
|
expect(last_response.status).to eq 400
|
126
128
|
expect(JSON.parse(last_response.body)).to eq(
|
127
|
-
'beer,
|
129
|
+
'beer,grapefruit' => ['are mutually exclusive']
|
128
130
|
)
|
129
131
|
end
|
130
132
|
end
|
@@ -137,7 +139,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
|
|
137
139
|
validate
|
138
140
|
expect(last_response.status).to eq 400
|
139
141
|
expect(JSON.parse(last_response.body)).to eq(
|
140
|
-
'beer,wine
|
142
|
+
'beer,wine' => ['you should choose one']
|
141
143
|
)
|
142
144
|
end
|
143
145
|
end
|
@@ -173,7 +175,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
|
|
173
175
|
validate
|
174
176
|
expect(last_response.status).to eq 400
|
175
177
|
expect(JSON.parse(last_response.body)).to eq(
|
176
|
-
'item[beer],item[wine]
|
178
|
+
'item[beer],item[wine]' => ['are mutually exclusive']
|
177
179
|
)
|
178
180
|
end
|
179
181
|
end
|
@@ -188,7 +190,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
|
|
188
190
|
validate
|
189
191
|
expect(last_response.status).to eq 400
|
190
192
|
expect(JSON.parse(last_response.body)).to eq(
|
191
|
-
'item[beer],item[wine]
|
193
|
+
'item[beer],item[wine]' => ['are mutually exclusive']
|
192
194
|
)
|
193
195
|
end
|
194
196
|
end
|
@@ -211,11 +213,11 @@ describe Grape::Validations::ExactlyOneOfValidator do
|
|
211
213
|
validate
|
212
214
|
expect(last_response.status).to eq 400
|
213
215
|
expect(JSON.parse(last_response.body)).to eq(
|
214
|
-
'items[0][beer],items[0][wine]
|
215
|
-
'are
|
216
|
+
'items[0][beer],items[0][wine]' => [
|
217
|
+
'are mutually exclusive'
|
216
218
|
],
|
217
|
-
'items[1][
|
218
|
-
'are
|
219
|
+
'items[1][wine],items[1][grapefruit]' => [
|
220
|
+
'are mutually exclusive'
|
219
221
|
]
|
220
222
|
)
|
221
223
|
end
|
@@ -229,8 +231,8 @@ describe Grape::Validations::ExactlyOneOfValidator do
|
|
229
231
|
validate
|
230
232
|
expect(last_response.status).to eq 400
|
231
233
|
expect(JSON.parse(last_response.body)).to eq(
|
232
|
-
'items[0][nested_items][0][beer],items[0][nested_items][0][wine]
|
233
|
-
'are
|
234
|
+
'items[0][nested_items][0][beer],items[0][nested_items][0][wine]' => [
|
235
|
+
'are mutually exclusive'
|
234
236
|
]
|
235
237
|
)
|
236
238
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Grape::Validations::ExceptValuesValidator do
|
@@ -110,7 +112,7 @@ describe Grape::Validations::ExceptValuesValidator do
|
|
110
112
|
optional: { type: Array[Integer], except_values: [10, 11], default: 12 },
|
111
113
|
tests: [
|
112
114
|
{ value: 'invalid-type1', rc: 400, body: { error: 'type is invalid' }.to_json },
|
113
|
-
{ value: 10, rc: 400, body: { error: 'type
|
115
|
+
{ value: 10, rc: 400, body: { error: 'type is invalid' }.to_json },
|
114
116
|
{ value: [10], rc: 400, body: { error: 'type has a value not allowed' }.to_json },
|
115
117
|
{ value: ['3'], rc: 200, body: { type: [3] }.to_json },
|
116
118
|
{ value: [3], rc: 200, body: { type: [3] }.to_json },
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Grape::Validations::PresenceValidator do
|
@@ -269,4 +271,32 @@ describe Grape::Validations::PresenceValidator do
|
|
269
271
|
expect(last_response.body).to eq('Hello optional'.to_json)
|
270
272
|
end
|
271
273
|
end
|
274
|
+
|
275
|
+
context 'with a custom type' do
|
276
|
+
it 'does not validate their type when it is missing' do
|
277
|
+
class CustomType
|
278
|
+
def self.parse(value)
|
279
|
+
return if value.blank?
|
280
|
+
|
281
|
+
new
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
subject.params do
|
286
|
+
requires :custom, type: CustomType
|
287
|
+
end
|
288
|
+
subject.get '/custom' do
|
289
|
+
'custom'
|
290
|
+
end
|
291
|
+
|
292
|
+
get 'custom'
|
293
|
+
|
294
|
+
expect(last_response.status).to eq(400)
|
295
|
+
expect(last_response.body).to eq('{"error":"custom is missing"}')
|
296
|
+
|
297
|
+
get 'custom', custom: 'filled'
|
298
|
+
|
299
|
+
expect(last_response.status).to eq(200)
|
300
|
+
end
|
301
|
+
end
|
272
302
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Grape::Validations::ValuesValidator do
|
@@ -222,6 +224,11 @@ describe Grape::Validations::ValuesValidator do
|
|
222
224
|
requires :type, values: { proc: ->(v) { ValuesModel.values.include? v }, message: 'failed check' }
|
223
225
|
end
|
224
226
|
get '/proc/message'
|
227
|
+
|
228
|
+
params do
|
229
|
+
optional :name, type: String, values: %w[a b], allow_blank: true
|
230
|
+
end
|
231
|
+
get '/allow_blank'
|
225
232
|
end
|
226
233
|
end
|
227
234
|
end
|
@@ -312,7 +319,7 @@ describe Grape::Validations::ValuesValidator do
|
|
312
319
|
expect(last_response.status).to eq 200
|
313
320
|
end
|
314
321
|
|
315
|
-
it '
|
322
|
+
it 'accepts for an optional param with a list of values' do
|
316
323
|
put('/optional_with_array_of_string_values', optional: nil)
|
317
324
|
expect(last_response.status).to eq 200
|
318
325
|
end
|
@@ -431,11 +438,21 @@ describe Grape::Validations::ValuesValidator do
|
|
431
438
|
end.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
432
439
|
end
|
433
440
|
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
441
|
+
context 'boolean values' do
|
442
|
+
it 'allows a value from the list' do
|
443
|
+
get('/values/optional_boolean', type: true)
|
444
|
+
|
445
|
+
expect(last_response.status).to eq 200
|
446
|
+
expect(last_response.body).to eq({ type: true }.to_json)
|
447
|
+
end
|
448
|
+
|
449
|
+
it 'rejects a value which is not in the list' do
|
450
|
+
get('/values/optional_boolean', type: false)
|
451
|
+
|
452
|
+
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
|
453
|
+
end
|
438
454
|
end
|
455
|
+
|
439
456
|
it 'allows values to be a kind of the coerced type not just an instance of it' do
|
440
457
|
get('/values/coercion', type: 10)
|
441
458
|
expect(last_response.status).to eq 200
|
@@ -462,6 +479,14 @@ describe Grape::Validations::ValuesValidator do
|
|
462
479
|
end.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
463
480
|
end
|
464
481
|
|
482
|
+
it 'allows a blank value when the allow_blank option is true' do
|
483
|
+
get 'allow_blank', name: nil
|
484
|
+
expect(last_response.status).to eq(200)
|
485
|
+
|
486
|
+
get 'allow_blank', name: ''
|
487
|
+
expect(last_response.status).to eq(200)
|
488
|
+
end
|
489
|
+
|
465
490
|
context 'with a lambda values' do
|
466
491
|
subject do
|
467
492
|
Class.new(Grape::API) do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Grape::Validations do
|
@@ -7,16 +9,23 @@ describe Grape::Validations do
|
|
7
9
|
subject
|
8
10
|
end
|
9
11
|
|
12
|
+
def declared_params
|
13
|
+
subject.namespace_stackable(:declared_params).flatten
|
14
|
+
end
|
15
|
+
|
10
16
|
describe 'params' do
|
11
17
|
context 'optional' do
|
12
|
-
|
18
|
+
before do
|
13
19
|
subject.params do
|
14
20
|
optional :a_number, regexp: /^[0-9]+$/
|
21
|
+
optional :attachment, type: File
|
15
22
|
end
|
16
23
|
subject.get '/optional' do
|
17
24
|
'optional works!'
|
18
25
|
end
|
26
|
+
end
|
19
27
|
|
28
|
+
it 'validates when params is present' do
|
20
29
|
get '/optional', a_number: 'string'
|
21
30
|
expect(last_response.status).to eq(400)
|
22
31
|
expect(last_response.body).to eq('a_number is invalid')
|
@@ -27,14 +36,7 @@ describe Grape::Validations do
|
|
27
36
|
end
|
28
37
|
|
29
38
|
it "doesn't validate when param not present" do
|
30
|
-
|
31
|
-
optional :a_number, regexp: /^[0-9]+$/
|
32
|
-
end
|
33
|
-
subject.get '/optional' do
|
34
|
-
'optional works!'
|
35
|
-
end
|
36
|
-
|
37
|
-
get '/optional'
|
39
|
+
get '/optional', a_number: nil, attachment: nil
|
38
40
|
expect(last_response.status).to eq(200)
|
39
41
|
expect(last_response.body).to eq('optional works!')
|
40
42
|
end
|
@@ -43,7 +45,7 @@ describe Grape::Validations do
|
|
43
45
|
subject.params do
|
44
46
|
optional :some_param
|
45
47
|
end
|
46
|
-
expect(
|
48
|
+
expect(declared_params).to eq([:some_param])
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
@@ -63,7 +65,7 @@ describe Grape::Validations do
|
|
63
65
|
|
64
66
|
it 'adds entity documentation to declared params' do
|
65
67
|
define_optional_using
|
66
|
-
expect(
|
68
|
+
expect(declared_params).to eq(%i[field_a field_b])
|
67
69
|
end
|
68
70
|
|
69
71
|
it 'works when field_a and field_b are not present' do
|
@@ -110,7 +112,7 @@ describe Grape::Validations do
|
|
110
112
|
subject.params do
|
111
113
|
requires :some_param
|
112
114
|
end
|
113
|
-
expect(
|
115
|
+
expect(declared_params).to eq([:some_param])
|
114
116
|
end
|
115
117
|
|
116
118
|
it 'works when required field is present but nil' do
|
@@ -120,6 +122,62 @@ describe Grape::Validations do
|
|
120
122
|
end
|
121
123
|
end
|
122
124
|
|
125
|
+
context 'requires with nested params' do
|
126
|
+
before do
|
127
|
+
subject.params do
|
128
|
+
requires :first_level, type: Hash do
|
129
|
+
optional :second_level, type: Array do
|
130
|
+
requires :value, type: Integer
|
131
|
+
optional :name, type: String
|
132
|
+
optional :third_level, type: Array do
|
133
|
+
requires :value, type: Integer
|
134
|
+
optional :name, type: String
|
135
|
+
optional :fourth_level, type: Array do
|
136
|
+
requires :value, type: Integer
|
137
|
+
optional :name, type: String
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
subject.put('/required') { 'required works' }
|
144
|
+
end
|
145
|
+
|
146
|
+
let(:request_params) do
|
147
|
+
{
|
148
|
+
first_level: {
|
149
|
+
second_level: [
|
150
|
+
{ value: 1, name: 'Lisa' },
|
151
|
+
{
|
152
|
+
value: 2,
|
153
|
+
name: 'James',
|
154
|
+
third_level: [
|
155
|
+
{ value: 'three', name: 'Sophie' },
|
156
|
+
{
|
157
|
+
value: 4,
|
158
|
+
name: 'Jenny',
|
159
|
+
fourth_level: [
|
160
|
+
{ name: 'Samuel' }, { value: 6, name: 'Jane' }
|
161
|
+
]
|
162
|
+
}
|
163
|
+
]
|
164
|
+
}
|
165
|
+
]
|
166
|
+
}
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'validates correctly in deep nested params' do
|
171
|
+
put '/required', request_params.to_json, 'CONTENT_TYPE' => 'application/json'
|
172
|
+
|
173
|
+
expect(last_response.status).to eq(400)
|
174
|
+
expect(last_response.body).to eq(
|
175
|
+
'first_level[second_level][1][third_level][0][value] is invalid, ' \
|
176
|
+
'first_level[second_level][1][third_level][1][fourth_level][0][value] is missing'
|
177
|
+
)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
123
181
|
context 'requires :all using Grape::Entity documentation' do
|
124
182
|
def define_requires_all
|
125
183
|
documentation = {
|
@@ -139,7 +197,7 @@ describe Grape::Validations do
|
|
139
197
|
|
140
198
|
it 'adds entity documentation to declared params' do
|
141
199
|
define_requires_all
|
142
|
-
expect(
|
200
|
+
expect(declared_params).to eq(%i[required_field optional_field])
|
143
201
|
end
|
144
202
|
|
145
203
|
it 'errors when required_field is not present' do
|
@@ -174,7 +232,7 @@ describe Grape::Validations do
|
|
174
232
|
|
175
233
|
it 'adds entity documentation to declared params' do
|
176
234
|
define_requires_none
|
177
|
-
expect(
|
235
|
+
expect(declared_params).to eq(%i[required_field optional_field])
|
178
236
|
end
|
179
237
|
|
180
238
|
it 'errors when required_field is not present' do
|
@@ -204,7 +262,7 @@ describe Grape::Validations do
|
|
204
262
|
|
205
263
|
it 'adds only the entity documentation to declared params, nothing more' do
|
206
264
|
define_requires_all
|
207
|
-
expect(
|
265
|
+
expect(declared_params).to eq(%i[required_field optional_field])
|
208
266
|
end
|
209
267
|
end
|
210
268
|
|
@@ -270,7 +328,7 @@ describe Grape::Validations do
|
|
270
328
|
requires :key
|
271
329
|
end
|
272
330
|
end
|
273
|
-
expect(
|
331
|
+
expect(declared_params).to eq([items: [:key]])
|
274
332
|
end
|
275
333
|
end
|
276
334
|
|
@@ -342,7 +400,7 @@ describe Grape::Validations do
|
|
342
400
|
requires :key
|
343
401
|
end
|
344
402
|
end
|
345
|
-
expect(
|
403
|
+
expect(declared_params).to eq([items: [:key]])
|
346
404
|
end
|
347
405
|
end
|
348
406
|
|
@@ -405,7 +463,7 @@ describe Grape::Validations do
|
|
405
463
|
requires :key
|
406
464
|
end
|
407
465
|
end
|
408
|
-
expect(
|
466
|
+
expect(declared_params).to eq([items: [:key]])
|
409
467
|
end
|
410
468
|
end
|
411
469
|
|
@@ -436,7 +494,7 @@ describe Grape::Validations do
|
|
436
494
|
class DateRangeValidator < Grape::Validations::Base
|
437
495
|
def validate_param!(attr_name, params)
|
438
496
|
return if params[attr_name][:from] <= params[attr_name][:to]
|
439
|
-
raise Grape::Exceptions::Validation
|
497
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'")
|
440
498
|
end
|
441
499
|
end
|
442
500
|
end
|
@@ -520,7 +578,7 @@ describe Grape::Validations do
|
|
520
578
|
# NOTE: with body parameters in json or XML or similar this
|
521
579
|
# should actually fail with: children[parents][name] is missing.
|
522
580
|
expect(last_response.status).to eq(400)
|
523
|
-
expect(last_response.body).to eq('children[1][parents] is missing')
|
581
|
+
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')
|
524
582
|
end
|
525
583
|
|
526
584
|
it 'errors when a parameter is not present in array within array' do
|
@@ -561,7 +619,7 @@ describe Grape::Validations do
|
|
561
619
|
|
562
620
|
get '/within_array', children: [name: 'Jay']
|
563
621
|
expect(last_response.status).to eq(400)
|
564
|
-
expect(last_response.body).to eq('children[0][parents] is missing')
|
622
|
+
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')
|
565
623
|
end
|
566
624
|
|
567
625
|
it 'errors when param is not an Array' do
|
@@ -709,7 +767,7 @@ describe Grape::Validations do
|
|
709
767
|
expect(last_response.status).to eq(200)
|
710
768
|
put_with_json '/within_array', children: [name: 'Jay']
|
711
769
|
expect(last_response.status).to eq(400)
|
712
|
-
expect(last_response.body).to eq('children[0][parents] is missing')
|
770
|
+
expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing')
|
713
771
|
end
|
714
772
|
end
|
715
773
|
|
@@ -759,7 +817,7 @@ describe Grape::Validations do
|
|
759
817
|
requires :key
|
760
818
|
end
|
761
819
|
end
|
762
|
-
expect(
|
820
|
+
expect(declared_params).to eq([items: [:key]])
|
763
821
|
end
|
764
822
|
end
|
765
823
|
|
@@ -784,7 +842,7 @@ describe Grape::Validations do
|
|
784
842
|
it 'does internal validations if the outer group is present' do
|
785
843
|
get '/nested_optional_group', items: [{ key: 'foo' }]
|
786
844
|
expect(last_response.status).to eq(400)
|
787
|
-
expect(last_response.body).to eq('items[0][required_subitems] is missing')
|
845
|
+
expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
|
788
846
|
|
789
847
|
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
|
790
848
|
expect(last_response.status).to eq(200)
|
@@ -804,7 +862,7 @@ describe Grape::Validations do
|
|
804
862
|
it 'handles validation within arrays' do
|
805
863
|
get '/nested_optional_group', items: [{ key: 'foo' }]
|
806
864
|
expect(last_response.status).to eq(400)
|
807
|
-
expect(last_response.body).to eq('items[0][required_subitems] is missing')
|
865
|
+
expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
|
808
866
|
|
809
867
|
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
|
810
868
|
expect(last_response.status).to eq(200)
|
@@ -823,7 +881,7 @@ describe Grape::Validations do
|
|
823
881
|
requires(:required_subitems, type: Array) { requires :value }
|
824
882
|
end
|
825
883
|
end
|
826
|
-
expect(
|
884
|
+
expect(declared_params).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
|
827
885
|
end
|
828
886
|
end
|
829
887
|
|
@@ -851,7 +909,7 @@ describe Grape::Validations do
|
|
851
909
|
class Customvalidator < Grape::Validations::Base
|
852
910
|
def validate_param!(attr_name, params)
|
853
911
|
return if params[attr_name] == 'im custom'
|
854
|
-
raise Grape::Exceptions::Validation
|
912
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: 'is not custom!')
|
855
913
|
end
|
856
914
|
end
|
857
915
|
end
|
@@ -999,7 +1057,7 @@ describe Grape::Validations do
|
|
999
1057
|
class CustomvalidatorWithOptions < Grape::Validations::Base
|
1000
1058
|
def validate_param!(attr_name, params)
|
1001
1059
|
return if params[attr_name] == @option[:text]
|
1002
|
-
raise Grape::Exceptions::Validation
|
1060
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
|
1003
1061
|
end
|
1004
1062
|
end
|
1005
1063
|
end
|
@@ -1068,14 +1126,14 @@ describe Grape::Validations do
|
|
1068
1126
|
subject.params do
|
1069
1127
|
use :pagination
|
1070
1128
|
end
|
1071
|
-
expect(
|
1129
|
+
expect(declared_params).to eq %i[page per_page]
|
1072
1130
|
end
|
1073
1131
|
|
1074
1132
|
it 'by #use with multiple params' do
|
1075
1133
|
subject.params do
|
1076
1134
|
use :pagination, :period
|
1077
1135
|
end
|
1078
|
-
expect(
|
1136
|
+
expect(declared_params).to eq %i[page per_page start_date end_date]
|
1079
1137
|
end
|
1080
1138
|
end
|
1081
1139
|
|
@@ -1364,7 +1422,7 @@ describe Grape::Validations do
|
|
1364
1422
|
it 'errors when two or more are present' do
|
1365
1423
|
get '/custom_message/exactly_one_of', beer: 'string', wine: 'anotherstring'
|
1366
1424
|
expect(last_response.status).to eq(400)
|
1367
|
-
expect(last_response.body).to eq 'beer, wine
|
1425
|
+
expect(last_response.body).to eq 'beer, wine are missing, exactly one parameter is required'
|
1368
1426
|
end
|
1369
1427
|
end
|
1370
1428
|
|
@@ -1383,7 +1441,7 @@ describe Grape::Validations do
|
|
1383
1441
|
it 'errors when two or more are present' do
|
1384
1442
|
get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
|
1385
1443
|
expect(last_response.status).to eq(400)
|
1386
|
-
expect(last_response.body).to eq 'beer, wine
|
1444
|
+
expect(last_response.body).to eq 'beer, wine are mutually exclusive'
|
1387
1445
|
end
|
1388
1446
|
end
|
1389
1447
|
|
@@ -1423,7 +1481,7 @@ describe Grape::Validations do
|
|
1423
1481
|
it 'errors when two or more are present' do
|
1424
1482
|
get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
|
1425
1483
|
expect(last_response.status).to eq(400)
|
1426
|
-
expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2]
|
1484
|
+
expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2] are mutually exclusive'
|
1427
1485
|
end
|
1428
1486
|
end
|
1429
1487
|
end
|