grape 1.2.5 → 1.3.1
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 +45 -0
- data/README.md +7 -6
- data/UPGRADING.md +43 -0
- data/grape.gemspec +10 -1
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +8 -6
- data/lib/grape/api.rb +4 -2
- 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 +15 -11
- 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 +2 -0
- data/lib/grape/eager_load.rb +2 -0
- data/lib/grape/endpoint.rb +13 -7
- 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/error_formatter.rb +3 -1
- 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/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/formatter.rb +5 -3
- data/lib/grape/http/headers.rb +49 -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 +5 -5
- data/lib/grape/middleware/error.rb +3 -1
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +5 -3
- 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/accept_version_header.rb +2 -0
- data/lib/grape/middleware/versioner/header.rb +5 -3
- data/lib/grape/middleware/versioner/param.rb +3 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -0
- data/lib/grape/middleware/versioner/path.rb +3 -1
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/namespace.rb +14 -2
- data/lib/grape/parser/json.rb +2 -0
- data/lib/grape/parser/xml.rb +2 -0
- data/lib/grape/parser.rb +3 -1
- data/lib/grape/path.rb +13 -1
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +15 -8
- data/lib/grape/router/attribute_translator.rb +18 -8
- data/lib/grape/router/pattern.rb +20 -16
- data/lib/grape/router/route.rb +9 -4
- data/lib/grape/router.rb +26 -12
- data/lib/grape/serve_file/file_body.rb +2 -0
- data/lib/grape/serve_file/file_response.rb +2 -0
- data/lib/grape/serve_file/sendfile_response.rb +2 -0
- data/lib/grape/util/base_inheritable.rb +6 -0
- 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 +3 -1
- data/lib/grape/util/stackable_values.rb +9 -21
- data/lib/grape/util/strict_hash_configuration.rb +2 -0
- data/lib/grape/util/xml.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 +24 -11
- data/lib/grape/validations/single_attribute_iterator.rb +13 -2
- data/lib/grape/validations/types/array_coercer.rb +56 -0
- data/lib/grape/validations/types/build_coercer.rb +49 -48
- 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 +41 -0
- data/lib/grape/validations/types/file.rb +11 -9
- data/lib/grape/validations/types/json.rb +11 -8
- data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
- data/lib/grape/validations/types/primitive_coercer.rb +61 -0
- data/lib/grape/validations/types/set_coercer.rb +38 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +4 -12
- data/lib/grape/validations/types.rb +7 -30
- 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 +44 -27
- data/lib/grape/validations/validators/default.rb +2 -0
- 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 +3 -1
- data/lib/grape/validations/validators/same_as.rb +6 -3
- data/lib/grape/validations/validators/values.rb +17 -5
- data/lib/grape/validations.rb +2 -0
- data/lib/grape/version.rb +3 -1
- data/lib/grape.rb +4 -5
- 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 +54 -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 +34 -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 +2 -0
- 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 +3 -1
- 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 +2 -0
- 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 +2 -0
- 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 +2 -0
- 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/primitive_coercer_spec.rb +75 -0
- data/spec/grape/validations/types_spec.rb +8 -35
- 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 +51 -110
- data/spec/grape/validations/validators/default_spec.rb +2 -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 +29 -4
- data/spec/grape/validations_spec.rb +69 -15
- 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 +126 -112
- 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,21 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
|
3
5
|
module Grape
|
4
6
|
module Validations
|
5
7
|
module Types
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# given as JSON-encoded strings. It accepts both JSON objects
|
8
|
+
# Handles coercion and type checking for parameters that are complex
|
9
|
+
# types given as JSON-encoded strings. It accepts both JSON objects
|
9
10
|
# and arrays of objects, and will coerce the input to a +Hash+
|
10
11
|
# or +Array+ object respectively. In either case the Grape
|
11
12
|
# validation system will apply nested validation rules to
|
12
13
|
# all returned objects.
|
13
|
-
class Json
|
14
|
+
class Json
|
14
15
|
# Coerce the input into a JSON-like data structure.
|
15
16
|
#
|
16
17
|
# @param input [String] a JSON-encoded parameter value
|
17
18
|
# @return [Hash,Array<Hash>,nil]
|
18
|
-
def
|
19
|
+
def call(input)
|
20
|
+
return input if coerced?(input)
|
21
|
+
|
19
22
|
# Allow nulls and blank strings
|
20
23
|
return if input.nil? || input =~ /^\s*$/
|
21
24
|
JSON.parse(input, symbolize_names: true)
|
@@ -26,7 +29,7 @@ module Grape
|
|
26
29
|
#
|
27
30
|
# @param value [Object] result of {#coerce}
|
28
31
|
# @return [true,false]
|
29
|
-
def
|
32
|
+
def coerced?(value)
|
30
33
|
value.is_a?(::Hash) || coerced_collection?(value)
|
31
34
|
end
|
32
35
|
|
@@ -50,13 +53,13 @@ module Grape
|
|
50
53
|
#
|
51
54
|
# @param input [String] JSON-encoded parameter value
|
52
55
|
# @return [Array<Hash>]
|
53
|
-
def
|
56
|
+
def call(input)
|
54
57
|
json = super
|
55
58
|
Array.wrap(json) unless json.nil?
|
56
59
|
end
|
57
60
|
|
58
61
|
# See {Json#coerced_collection?}
|
59
|
-
def
|
62
|
+
def coerced?(value)
|
60
63
|
coerced_collection? value
|
61
64
|
end
|
62
65
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
module Types
|
@@ -22,53 +24,32 @@ module Grape
|
|
22
24
|
|
23
25
|
@type_coercers = types.map do |type|
|
24
26
|
if Types.multiple? type
|
25
|
-
VariantCollectionCoercer.new type
|
27
|
+
VariantCollectionCoercer.new type, @method
|
26
28
|
else
|
27
|
-
Types.build_coercer type
|
29
|
+
Types.build_coercer type, strict: !@method.nil?
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
|
-
#
|
33
|
-
# +Virtus::Attribute::coerce+ in order to coerce
|
34
|
-
# the given value.
|
34
|
+
# Coerces the given value.
|
35
35
|
#
|
36
|
-
# @param
|
36
|
+
# @param val [String] value to be coerced, in grape
|
37
37
|
# this should always be a string.
|
38
38
|
# @return [Object,InvalidValue] the coerced result, or an instance
|
39
39
|
# of {InvalidValue} if the value could not be coerced.
|
40
|
-
def call(
|
41
|
-
|
40
|
+
def call(val)
|
41
|
+
# once the value is coerced by the custom method, its type should be checked
|
42
|
+
val = @method.call(val) if @method
|
43
|
+
|
44
|
+
coerced_val = InvalidValue.new
|
42
45
|
|
43
46
|
@type_coercers.each do |coercer|
|
44
|
-
|
47
|
+
coerced_val = coercer.call(val)
|
45
48
|
|
46
|
-
return
|
49
|
+
return coerced_val unless coerced_val.is_a?(InvalidValue)
|
47
50
|
end
|
48
51
|
|
49
|
-
|
50
|
-
# that Grape won't ask us again if the value is valid
|
51
|
-
InvalidValue.new
|
52
|
-
end
|
53
|
-
|
54
|
-
# This method is called from somewhere within
|
55
|
-
# +Virtus::Attribute::value_coerced?+ in order to
|
56
|
-
# assert that the value has been coerced successfully.
|
57
|
-
# Due to Grape's design this will in fact only be called
|
58
|
-
# if a custom coercion method is being used, since {#call}
|
59
|
-
# returns an {InvalidValue} object if the value could not
|
60
|
-
# be coerced.
|
61
|
-
#
|
62
|
-
# @param _primitive [Axiom::Types::Type] primitive type
|
63
|
-
# for the coercion as detected by axiom-types' inference
|
64
|
-
# system. For custom types this is typically not much use
|
65
|
-
# (i.e. it is +Axiom::Types::Object+) unless special
|
66
|
-
# inference rules have been declared for the type.
|
67
|
-
# @param value [Object] a coerced result returned from {#call}
|
68
|
-
# @return [true,false] whether or not the coerced value
|
69
|
-
# satisfies type requirements.
|
70
|
-
def success?(_primitive, value)
|
71
|
-
@type_coercers.any? { |coercer| coercer.value_coerced? value }
|
52
|
+
coerced_val
|
72
53
|
end
|
73
54
|
end
|
74
55
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'dry_type_coercer'
|
4
|
+
|
5
|
+
module Grape
|
6
|
+
module Validations
|
7
|
+
module Types
|
8
|
+
# Coerces the given value to a type defined via a +type+ argument during
|
9
|
+
# initialization. When +strict+ is true, it doesn't coerce a value but check
|
10
|
+
# that it has the proper type.
|
11
|
+
class PrimitiveCoercer < DryTypeCoercer
|
12
|
+
MAPPING = {
|
13
|
+
Grape::API::Boolean => DryTypes::Params::Bool,
|
14
|
+
|
15
|
+
# unfortunately, a +Params+ scope doesn't contain String
|
16
|
+
String => DryTypes::Coercible::String,
|
17
|
+
BigDecimal => DryTypes::Coercible::Decimal
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
STRICT_MAPPING = {
|
21
|
+
Grape::API::Boolean => DryTypes::Strict::Bool,
|
22
|
+
BigDecimal => DryTypes::Strict::Decimal
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
def initialize(type, strict = false)
|
26
|
+
super
|
27
|
+
|
28
|
+
@type = type
|
29
|
+
|
30
|
+
@coercer = if strict
|
31
|
+
STRICT_MAPPING.fetch(type) { scope.const_get(type.name) }
|
32
|
+
else
|
33
|
+
MAPPING.fetch(type) { scope.const_get(type.name) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(val)
|
38
|
+
return InvalidValue.new if reject?(val)
|
39
|
+
return nil if val.nil?
|
40
|
+
return '' if val == ''
|
41
|
+
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
attr_reader :type
|
48
|
+
|
49
|
+
# This method maintaine logic which was defined by Virtus. For example,
|
50
|
+
# dry-types is ok to convert an array or a hash to a string, it is supported,
|
51
|
+
# but Virtus wouldn't accept it. So, this method only exists to not introduce
|
52
|
+
# breaking changes.
|
53
|
+
def reject?(val)
|
54
|
+
(val.is_a?(Array) && type == String) ||
|
55
|
+
(val.is_a?(String) && type == Hash) ||
|
56
|
+
(val.is_a?(Hash) && type == String)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require_relative 'dry_type_coercer'
|
5
|
+
|
6
|
+
module Grape
|
7
|
+
module Validations
|
8
|
+
module Types
|
9
|
+
# Takes the given array and converts it to a set. Every element of the set
|
10
|
+
# is also coerced.
|
11
|
+
class SetCoercer < DryTypeCoercer
|
12
|
+
def initialize(type, strict = false)
|
13
|
+
super
|
14
|
+
|
15
|
+
@elem_coercer = PrimitiveCoercer.new(type.first, strict)
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(value)
|
19
|
+
return InvalidValue.new unless value.is_a?(Array)
|
20
|
+
|
21
|
+
coerce_elements(value)
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def coerce_elements(collection)
|
27
|
+
collection.each_with_object(Set.new) do |elem, memo|
|
28
|
+
coerced_elem = @elem_coercer.call(elem)
|
29
|
+
|
30
|
+
return coerced_elem if coerced_elem.is_a?(InvalidValue)
|
31
|
+
|
32
|
+
memo.add(coerced_elem)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
module Types
|
4
6
|
# This class wraps {MultipleTypeCoercer}, for use with collections
|
5
7
|
# that allow members of more than one type.
|
6
|
-
class VariantCollectionCoercer
|
8
|
+
class VariantCollectionCoercer
|
7
9
|
# Construct a new coercer that will attempt to coerce
|
8
10
|
# a list of values such that all members are of one of
|
9
11
|
# the given types. The container may also optionally be
|
@@ -30,7 +32,7 @@ module Grape
|
|
30
32
|
# @return [Array<Object>,Set<Object>,InvalidValue]
|
31
33
|
# the coerced result, or an instance
|
32
34
|
# of {InvalidValue} if the value could not be coerced.
|
33
|
-
def
|
35
|
+
def call(value)
|
34
36
|
return InvalidValue.new unless value.is_a? Array
|
35
37
|
|
36
38
|
value =
|
@@ -43,16 +45,6 @@ module Grape
|
|
43
45
|
|
44
46
|
value
|
45
47
|
end
|
46
|
-
|
47
|
-
# Assert that the value has been coerced successfully.
|
48
|
-
#
|
49
|
-
# @param value [Object] a coerced result returned from {#coerce}
|
50
|
-
# @return [true,false] whether or not the coerced value
|
51
|
-
# satisfies type requirements.
|
52
|
-
def value_coerced?(value)
|
53
|
-
value.is_a?(@types.class) &&
|
54
|
-
value.all? { |v| @member_coercer.success?(@types, v) }
|
55
|
-
end
|
56
48
|
end
|
57
49
|
end
|
58
50
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'types/build_coercer'
|
2
4
|
require_relative 'types/custom_type_coercer'
|
3
5
|
require_relative 'types/custom_type_collection_coercer'
|
@@ -6,10 +8,6 @@ require_relative 'types/variant_collection_coercer'
|
|
6
8
|
require_relative 'types/json'
|
7
9
|
require_relative 'types/file'
|
8
10
|
|
9
|
-
# Patch for Virtus::Attribute::Collection
|
10
|
-
# See the file for more details
|
11
|
-
require_relative 'types/virtus_collection_patch'
|
12
|
-
|
13
11
|
module Grape
|
14
12
|
module Validations
|
15
13
|
# Module for code related to grape's system for
|
@@ -27,8 +25,7 @@ module Grape
|
|
27
25
|
# a parameter value could not be coerced.
|
28
26
|
class InvalidValue; end
|
29
27
|
|
30
|
-
# Types representing a single value, which are coerced
|
31
|
-
# or special logic in Grape.
|
28
|
+
# Types representing a single value, which are coerced.
|
32
29
|
PRIMITIVES = [
|
33
30
|
# Numerical
|
34
31
|
Integer,
|
@@ -42,10 +39,12 @@ module Grape
|
|
42
39
|
Time,
|
43
40
|
|
44
41
|
# Misc
|
45
|
-
|
42
|
+
Grape::API::Boolean,
|
46
43
|
String,
|
47
44
|
Symbol,
|
48
|
-
Rack::Multipart::UploadedFile
|
45
|
+
Rack::Multipart::UploadedFile,
|
46
|
+
TrueClass,
|
47
|
+
FalseClass
|
49
48
|
].freeze
|
50
49
|
|
51
50
|
# Types representing data structures.
|
@@ -86,8 +85,6 @@ module Grape
|
|
86
85
|
# @param type [Class] type to check
|
87
86
|
# @return [Boolean] whether or not the type is known by Grape as a valid
|
88
87
|
# data structure type
|
89
|
-
# @note This method does not yet consider 'complex types', which inherit
|
90
|
-
# Virtus.model.
|
91
88
|
def self.structure?(type)
|
92
89
|
STRUCTURES.include?(type)
|
93
90
|
end
|
@@ -104,25 +101,6 @@ module Grape
|
|
104
101
|
(type.is_a?(Array) || type.is_a?(Set)) && type.size > 1
|
105
102
|
end
|
106
103
|
|
107
|
-
# Does the given class implement a type system that Grape
|
108
|
-
# (i.e. the underlying virtus attribute system) supports
|
109
|
-
# out-of-the-box? Currently supported are +axiom-types+
|
110
|
-
# and +virtus+.
|
111
|
-
#
|
112
|
-
# The type will be passed to +Virtus::Attribute.build+,
|
113
|
-
# and the resulting attribute object will be expected to
|
114
|
-
# respond correctly to +coerce+ and +value_coerced?+.
|
115
|
-
#
|
116
|
-
# @param type [Class] type to check
|
117
|
-
# @return [Boolean] +true+ where the type is recognized
|
118
|
-
def self.recognized?(type)
|
119
|
-
return false if type.is_a?(Array) || type.is_a?(Set)
|
120
|
-
|
121
|
-
type.is_a?(Virtus::Attribute) ||
|
122
|
-
type.ancestors.include?(Axiom::Types::Type) ||
|
123
|
-
type.include?(Virtus::Model::Core)
|
124
|
-
end
|
125
|
-
|
126
104
|
# Does Grape provide special coercion and validation
|
127
105
|
# routines for the given class? This does not include
|
128
106
|
# automatic handling for primitives, structures and
|
@@ -152,7 +130,6 @@ module Grape
|
|
152
130
|
!primitive?(type) &&
|
153
131
|
!structure?(type) &&
|
154
132
|
!multiple?(type) &&
|
155
|
-
!recognized?(type) &&
|
156
133
|
!special?(type) &&
|
157
134
|
type.respond_to?(:parse) &&
|
158
135
|
type.method(:parse).arity == 1
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape/validations/validators/multiple_params_base'
|
2
4
|
|
3
5
|
module Grape
|
@@ -6,7 +8,7 @@ module Grape
|
|
6
8
|
def validate_params!(params)
|
7
9
|
keys = keys_in_common(params)
|
8
10
|
return if keys.empty? || keys.length == all_keys.length
|
9
|
-
raise Grape::Exceptions::Validation
|
11
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:all_or_none))
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
class AllowBlankValidator < Base
|
@@ -9,7 +11,7 @@ module Grape
|
|
9
11
|
|
10
12
|
return if value == false || value.present?
|
11
13
|
|
12
|
-
raise Grape::Exceptions::Validation
|
14
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:blank))
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape/validations/validators/multiple_params_base'
|
2
4
|
|
3
5
|
module Grape
|
@@ -5,7 +7,7 @@ module Grape
|
|
5
7
|
class AtLeastOneOfValidator < MultipleParamsBase
|
6
8
|
def validate_params!(params)
|
7
9
|
return unless keys_in_common(params).empty?
|
8
|
-
raise Grape::Exceptions::Validation
|
10
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:at_least_one))
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
class Base
|
@@ -17,6 +19,7 @@ module Grape
|
|
17
19
|
@required = required
|
18
20
|
@scope = scope
|
19
21
|
@fail_fast = opts[:fail_fast] || false
|
22
|
+
@allow_blank = opts[:allow_blank] || false
|
20
23
|
end
|
21
24
|
|
22
25
|
# Validates a given request.
|
@@ -40,12 +43,12 @@ module Grape
|
|
40
43
|
# there may be more than one error per field
|
41
44
|
array_errors = []
|
42
45
|
|
43
|
-
attributes.each do |
|
44
|
-
next if !@scope.required? &&
|
45
|
-
next unless @scope.meets_dependency?(
|
46
|
+
attributes.each do |val, attr_name, empty_val|
|
47
|
+
next if !@scope.required? && empty_val
|
48
|
+
next unless @scope.meets_dependency?(val, params)
|
46
49
|
begin
|
47
|
-
if @required ||
|
48
|
-
validate_param!(attr_name,
|
50
|
+
if @required || val.respond_to?(:key?) && val.key?(attr_name)
|
51
|
+
validate_param!(attr_name, val)
|
49
52
|
end
|
50
53
|
rescue Grape::Exceptions::Validation => e
|
51
54
|
array_errors << e
|
@@ -1,9 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
class API
|
3
|
-
|
5
|
+
class Boolean
|
6
|
+
def self.build(val)
|
7
|
+
return nil if val != true && val != false
|
8
|
+
|
9
|
+
new
|
10
|
+
end
|
11
|
+
end
|
4
12
|
|
5
13
|
class Instance
|
6
|
-
Boolean =
|
14
|
+
Boolean = Grape::API::Boolean
|
7
15
|
end
|
8
16
|
end
|
9
17
|
|
@@ -11,7 +19,12 @@ module Grape
|
|
11
19
|
class CoerceValidator < Base
|
12
20
|
def initialize(*_args)
|
13
21
|
super
|
14
|
-
|
22
|
+
|
23
|
+
@converter = if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
|
24
|
+
type
|
25
|
+
else
|
26
|
+
Types.build_coercer(type, method: @option[:method])
|
27
|
+
end
|
15
28
|
end
|
16
29
|
|
17
30
|
def validate(request)
|
@@ -19,11 +32,22 @@ module Grape
|
|
19
32
|
end
|
20
33
|
|
21
34
|
def validate_param!(attr_name, params)
|
22
|
-
raise
|
23
|
-
|
35
|
+
raise validation_exception(attr_name) unless params.is_a? Hash
|
36
|
+
|
24
37
|
new_value = coerce_value(params[attr_name])
|
25
|
-
|
26
|
-
|
38
|
+
|
39
|
+
raise validation_exception(attr_name) unless valid_type?(new_value)
|
40
|
+
|
41
|
+
# Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
|
42
|
+
# which looses wrappers for hashes and arrays after reassigning values
|
43
|
+
#
|
44
|
+
# h = Hashie::Mash.new(list: [1, 2, 3, 4])
|
45
|
+
# => #<Hashie::Mash list=#<Hashie::Array [1, 2, 3, 4]>>
|
46
|
+
# list = h.list
|
47
|
+
# h[:list] = list
|
48
|
+
# h
|
49
|
+
# => #<Hashie::Mash list=[1, 2, 3, 4]>
|
50
|
+
params[attr_name] = new_value unless params[attr_name] == new_value
|
27
51
|
end
|
28
52
|
|
29
53
|
private
|
@@ -33,32 +57,26 @@ module Grape
|
|
33
57
|
#
|
34
58
|
# See {Types.build_coercer}
|
35
59
|
#
|
36
|
-
# @return [
|
60
|
+
# @return [Object]
|
37
61
|
attr_reader :converter
|
38
62
|
|
39
63
|
def valid_type?(val)
|
40
|
-
|
41
|
-
return false if val.instance_of?(Types::InvalidValue)
|
42
|
-
|
43
|
-
# Allow nil, to ignore when a parameter is absent
|
44
|
-
return true if val.nil?
|
45
|
-
|
46
|
-
converter.value_coerced? val
|
64
|
+
!val.is_a?(Types::InvalidValue)
|
47
65
|
end
|
48
66
|
|
49
67
|
def coerce_value(val)
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
return
|
54
|
-
return
|
68
|
+
# define default values for structures, the dry-types lib which is used
|
69
|
+
# for coercion doesn't accept nil as a value, so it would fail
|
70
|
+
if val.nil?
|
71
|
+
return [] if type == Array || type.is_a?(Array)
|
72
|
+
return Set.new if type == Set
|
73
|
+
return {} if type == Hash
|
55
74
|
end
|
56
75
|
|
57
|
-
converter.
|
76
|
+
converter.call(val)
|
58
77
|
|
59
|
-
#
|
60
|
-
|
61
|
-
rescue
|
78
|
+
# Some custom types might fail, so it should be treated as an invalid value
|
79
|
+
rescue StandardError
|
62
80
|
Types::InvalidValue.new
|
63
81
|
end
|
64
82
|
|
@@ -69,9 +87,8 @@ module Grape
|
|
69
87
|
@option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
|
70
88
|
end
|
71
89
|
|
72
|
-
def
|
73
|
-
|
74
|
-
!valid_type?(value) || converter.coercer.respond_to?(:method) && !converter.is_a?(Grape::Validations::Types::Json)
|
90
|
+
def validation_exception(attr_name)
|
91
|
+
Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:coerce))
|
75
92
|
end
|
76
93
|
end
|
77
94
|
end
|
@@ -1,11 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape/validations/validators/multiple_params_base'
|
2
4
|
|
3
5
|
module Grape
|
4
6
|
module Validations
|
5
7
|
class ExactlyOneOfValidator < MultipleParamsBase
|
6
8
|
def validate_params!(params)
|
7
|
-
|
8
|
-
|
9
|
+
keys = keys_in_common(params)
|
10
|
+
return if keys.length == 1
|
11
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.length.zero?
|
12
|
+
raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
|
9
13
|
end
|
10
14
|
end
|
11
15
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
class ExceptValuesValidator < Base
|
@@ -13,7 +15,7 @@ module Grape
|
|
13
15
|
return if excepts.nil?
|
14
16
|
|
15
17
|
param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
|
16
|
-
raise Grape::Exceptions::Validation
|
18
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:except_values)) if param_array.any? { |param| excepts.include?(param) }
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape/validations/validators/multiple_params_base'
|
2
4
|
|
3
5
|
module Grape
|
@@ -6,7 +8,7 @@ module Grape
|
|
6
8
|
def validate_params!(params)
|
7
9
|
keys = keys_in_common(params)
|
8
10
|
return if keys.length <= 1
|
9
|
-
raise Grape::Exceptions::Validation
|
11
|
+
raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
class PresenceValidator < Base
|
4
6
|
def validate_param!(attr_name, params)
|
5
7
|
return if params.respond_to?(:key?) && params.key?(attr_name)
|
6
|
-
raise Grape::Exceptions::Validation
|
8
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
|
7
9
|
end
|
8
10
|
end
|
9
11
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
class RegexpValidator < Base
|
4
6
|
def validate_param!(attr_name, params)
|
5
7
|
return unless params.respond_to?(:key?) && params.key?(attr_name)
|
6
8
|
return if Array.wrap(params[attr_name]).all? { |param| param.nil? || (param.to_s =~ (options_key?(:value) ? @option[:value] : @option)) }
|
7
|
-
raise Grape::Exceptions::Validation
|
9
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
|
8
10
|
end
|
9
11
|
end
|
10
12
|
end
|
@@ -1,12 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
class SameAsValidator < Base
|
4
6
|
def validate_param!(attr_name, params)
|
5
7
|
confirmation = options_key?(:value) ? @option[:value] : @option
|
6
8
|
return if params[attr_name] == params[confirmation]
|
7
|
-
raise Grape::Exceptions::Validation
|
8
|
-
|
9
|
-
|
9
|
+
raise Grape::Exceptions::Validation.new(
|
10
|
+
params: [@scope.full_name(attr_name)],
|
11
|
+
message: build_message
|
12
|
+
)
|
10
13
|
end
|
11
14
|
|
12
15
|
private
|