grape 2.4.0 → 3.2.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 +109 -0
- data/CONTRIBUTING.md +2 -10
- data/README.md +137 -181
- data/UPGRADING.md +154 -0
- data/grape.gemspec +4 -4
- data/lib/grape/api/instance.rb +51 -104
- data/lib/grape/api.rb +29 -42
- data/lib/grape/content_types.rb +1 -4
- data/lib/grape/declared_params_handler.rb +118 -0
- data/lib/grape/dry_types.rb +48 -4
- data/lib/grape/dsl/callbacks.rb +8 -58
- data/lib/grape/dsl/declared.rb +35 -0
- data/lib/grape/dsl/desc.rb +8 -67
- data/lib/grape/dsl/helpers.rb +59 -64
- data/lib/grape/dsl/inside_route.rb +28 -189
- data/lib/grape/dsl/logger.rb +3 -6
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +24 -51
- data/lib/grape/dsl/request_response.rb +136 -139
- data/lib/grape/dsl/routing.rb +240 -200
- data/lib/grape/dsl/settings.rb +23 -135
- data/lib/grape/dsl/validations.rb +38 -44
- data/lib/grape/endpoint.rb +169 -205
- data/lib/grape/error_formatter/base.rb +4 -2
- data/lib/grape/exceptions/base.rb +19 -45
- 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/method_not_allowed.rb +1 -1
- data/lib/grape/exceptions/missing_group_type.rb +0 -2
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/request_error.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +1 -1
- data/lib/grape/exceptions/unknown_parameter.rb +1 -1
- data/lib/grape/exceptions/unknown_params_builder.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
- data/lib/grape/exceptions/validation.rb +7 -4
- data/lib/grape/exceptions/validation_errors.rb +13 -7
- data/lib/grape/locale/en.yml +0 -5
- data/lib/grape/middleware/auth/base.rb +2 -0
- data/lib/grape/middleware/auth/dsl.rb +9 -10
- data/lib/grape/middleware/base.rb +6 -4
- data/lib/grape/middleware/error.rb +4 -14
- data/lib/grape/middleware/formatter.rb +11 -7
- data/lib/grape/middleware/stack.rb +3 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -3
- data/lib/grape/middleware/versioner/base.rb +42 -40
- data/lib/grape/middleware/versioner/header.rb +2 -18
- data/lib/grape/middleware/versioner/param.rb +2 -2
- data/lib/grape/middleware/versioner/path.rb +2 -2
- data/lib/grape/namespace.rb +15 -8
- data/lib/grape/params_builder/base.rb +2 -0
- data/lib/grape/params_builder.rb +2 -19
- data/lib/grape/request.rb +2 -10
- data/lib/grape/router/base_route.rb +14 -5
- data/lib/grape/router/greedy_route.rb +11 -5
- data/lib/grape/router/pattern.rb +6 -20
- data/lib/grape/router/route.rb +7 -11
- data/lib/grape/router.rb +42 -65
- data/lib/grape/util/api_description.rb +58 -0
- data/lib/grape/util/base_inheritable.rb +5 -2
- data/lib/grape/util/cache.rb +1 -0
- data/lib/grape/util/deep_freeze.rb +35 -0
- data/lib/grape/util/inheritable_setting.rb +8 -1
- data/lib/grape/util/media_type.rb +2 -2
- data/lib/grape/util/registry.rb +1 -1
- data/lib/grape/util/translation.rb +42 -0
- data/lib/grape/validations/attributes_iterator.rb +35 -20
- data/lib/grape/validations/contract_scope.rb +2 -8
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/param_scope_tracker.rb +57 -0
- data/lib/grape/validations/params_documentation.rb +50 -0
- data/lib/grape/validations/params_scope.rb +147 -156
- data/lib/grape/validations/single_attribute_iterator.rb +2 -2
- data/lib/grape/validations/types/array_coercer.rb +2 -3
- data/lib/grape/validations/types/dry_type_coercer.rb +4 -11
- data/lib/grape/validations/types/primitive_coercer.rb +1 -28
- data/lib/grape/validations/types.rb +10 -25
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
- data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
- data/lib/grape/validations/validators/base.rb +95 -25
- data/lib/grape/validations/validators/coerce_validator.rb +15 -35
- data/lib/grape/validations/validators/contract_scope_validator.rb +9 -5
- data/lib/grape/validations/validators/default_validator.rb +12 -18
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
- data/lib/grape/validations/validators/except_values_validator.rb +13 -4
- data/lib/grape/validations/validators/length_validator.rb +21 -22
- data/lib/grape/validations/validators/multiple_params_base.rb +5 -5
- data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
- data/lib/grape/validations/validators/presence_validator.rb +4 -2
- data/lib/grape/validations/validators/regexp_validator.rb +11 -3
- data/lib/grape/validations/validators/same_as_validator.rb +6 -15
- data/lib/grape/validations/validators/values_validator.rb +29 -21
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +29 -23
- metadata +31 -26
- data/lib/grape/api/helpers.rb +0 -9
- data/lib/grape/dsl/api.rb +0 -17
- data/lib/grape/dsl/configuration.rb +0 -15
- data/lib/grape/exceptions/conflicting_types.rb +0 -11
- data/lib/grape/exceptions/empty_message_body.rb +0 -11
- data/lib/grape/exceptions/invalid_parameters.rb +0 -11
- data/lib/grape/exceptions/missing_option.rb +0 -11
- data/lib/grape/exceptions/too_deep_parameters.rb +0 -11
- data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
- data/lib/grape/exceptions/unknown_options.rb +0 -11
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -24
- data/lib/grape/extensions/hash.rb +0 -27
- data/lib/grape/extensions/hashie/mash.rb +0 -24
- data/lib/grape/types/invalid_value.rb +0 -8
- data/lib/grape/util/strict_hash_configuration.rb +0 -108
- data/lib/grape/validations/attributes_doc.rb +0 -60
- data/lib/grape/validations/validator_factory.rb +0 -15
|
@@ -155,9 +155,10 @@ module Grape
|
|
|
155
155
|
# @return [Object] object to be used
|
|
156
156
|
# for coercion and type validation
|
|
157
157
|
def build_coercer(type, method: nil, strict: false)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
# no cache since unique
|
|
159
|
+
return create_coercer_instance(type, method, strict) if method.respond_to?(:call)
|
|
160
|
+
|
|
161
|
+
CoercerCache[[type, method, strict]]
|
|
161
162
|
end
|
|
162
163
|
|
|
163
164
|
def create_coercer_instance(type, method, strict)
|
|
@@ -184,30 +185,14 @@ module Grape
|
|
|
184
185
|
end
|
|
185
186
|
end
|
|
186
187
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
@__cache_write_lock.synchronize do
|
|
195
|
-
@__cache[key] = instance
|
|
188
|
+
class CoercerCache < Grape::Util::Cache
|
|
189
|
+
def initialize
|
|
190
|
+
super
|
|
191
|
+
@cache = Hash.new do |h, (type, method, strict)|
|
|
192
|
+
h[[type, method, strict]] = Grape::Validations::Types.create_coercer_instance(type, method, strict)
|
|
193
|
+
end
|
|
196
194
|
end
|
|
197
|
-
|
|
198
|
-
instance
|
|
199
195
|
end
|
|
200
|
-
|
|
201
|
-
def cache_key(type, method, strict)
|
|
202
|
-
[type, method, strict].each_with_object(+'_') do |val, memo|
|
|
203
|
-
next if val.nil?
|
|
204
|
-
|
|
205
|
-
memo << '_' << val.to_s
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
instance_variable_set(:@__cache, {})
|
|
210
|
-
instance_variable_set(:@__cache_write_lock, Mutex.new)
|
|
211
196
|
end
|
|
212
197
|
end
|
|
213
198
|
end
|
|
@@ -4,11 +4,14 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class AllOrNoneOfValidator < MultipleParamsBase
|
|
7
|
+
default_message_key :all_or_none
|
|
8
|
+
|
|
7
9
|
def validate_params!(params)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
known_keys = all_keys
|
|
11
|
+
keys = keys_in_common(params, known_keys)
|
|
12
|
+
return if keys.empty? || keys.length == @attrs.length
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
validation_error!(known_keys)
|
|
12
15
|
end
|
|
13
16
|
end
|
|
14
17
|
end
|
|
@@ -4,15 +4,20 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class AllowBlankValidator < Base
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
default_message_key :blank
|
|
8
|
+
|
|
9
|
+
def initialize(attrs, options, required, scope, opts)
|
|
10
|
+
super
|
|
11
|
+
@value = option_value
|
|
12
|
+
end
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
def validate_param!(attr_name, params)
|
|
15
|
+
return if @value || !hash_like?(params)
|
|
12
16
|
|
|
17
|
+
value = scrub(params[attr_name])
|
|
13
18
|
return if value == false || value.present?
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
validation_error!(attr_name)
|
|
16
21
|
end
|
|
17
22
|
end
|
|
18
23
|
end
|
|
@@ -4,10 +4,13 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class AtLeastOneOfValidator < MultipleParamsBase
|
|
7
|
+
default_message_key :at_least_one
|
|
8
|
+
|
|
7
9
|
def validate_params!(params)
|
|
8
|
-
|
|
10
|
+
known_keys = all_keys
|
|
11
|
+
return if hash_like?(params) && known_keys.intersect?(params.keys.map { |attr| @scope.full_name(attr) })
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
validation_error!(known_keys)
|
|
11
14
|
end
|
|
12
15
|
end
|
|
13
16
|
end
|
|
@@ -3,24 +3,64 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
|
+
# Base class for all parameter validators.
|
|
7
|
+
#
|
|
8
|
+
# == Freeze contract
|
|
9
|
+
# Validator instances are shared across requests and are frozen after
|
|
10
|
+
# initialization (via +.new+). All inputs (+options+, +opts+, +attrs+)
|
|
11
|
+
# arrive pre-frozen from the DSL boundary, so subclass ivars derived
|
|
12
|
+
# from them are frozen by construction. Lazy ivar assignment
|
|
13
|
+
# (e.g. +memoize+, <tt>||=</tt>) will raise +FrozenError+ at request time.
|
|
6
14
|
class Base
|
|
15
|
+
include Grape::Util::Translation
|
|
16
|
+
|
|
7
17
|
attr_reader :attrs
|
|
8
18
|
|
|
19
|
+
class << self
|
|
20
|
+
# Declares the default I18n message key used by +validation_error!+.
|
|
21
|
+
# Subclasses that only need a single fixed error message can declare it
|
|
22
|
+
# at the class level instead of overriding +initialize+:
|
|
23
|
+
#
|
|
24
|
+
# class MyValidator < Grape::Validations::Validators::Base
|
|
25
|
+
# default_message_key :my_error
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# The key is resolved through +message+, so a per-option +:message+
|
|
29
|
+
# override still takes precedence.
|
|
30
|
+
def default_message_key(key = nil)
|
|
31
|
+
if key
|
|
32
|
+
@default_message_key = key
|
|
33
|
+
else
|
|
34
|
+
@default_message_key || (superclass.respond_to?(:default_message_key) ? superclass.default_message_key : nil)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def new(...)
|
|
39
|
+
super.freeze
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def inherited(klass)
|
|
43
|
+
super
|
|
44
|
+
Validations.register(klass)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
9
48
|
# Creates a new Validator from options specified
|
|
10
49
|
# by a +requires+ or +optional+ directive during
|
|
11
50
|
# parameter definition.
|
|
12
51
|
# @param attrs [Array] names of attributes to which the Validator applies
|
|
13
|
-
# @param options [Object] implementation-dependent Validator options
|
|
52
|
+
# @param options [Object] implementation-dependent Validator options; deep-frozen on assignment
|
|
14
53
|
# @param required [Boolean] attribute(s) are required or optional
|
|
15
54
|
# @param scope [ParamsScope] parent scope for this Validator
|
|
16
55
|
# @param opts [Hash] additional validation options
|
|
17
56
|
def initialize(attrs, options, required, scope, opts)
|
|
18
|
-
@attrs = Array(attrs)
|
|
19
|
-
@
|
|
57
|
+
@attrs = Array(attrs).freeze
|
|
58
|
+
@options = Grape::Util::DeepFreeze.deep_freeze(options)
|
|
59
|
+
@option = @options # TODO: remove in next major release
|
|
20
60
|
@required = required
|
|
21
61
|
@scope = scope
|
|
22
|
-
@fail_fast = opts
|
|
23
|
-
@
|
|
62
|
+
@fail_fast, @allow_blank = opts.values_at(:fail_fast, :allow_blank)
|
|
63
|
+
@exception_message = message(self.class.default_message_key) if self.class.default_message_key
|
|
24
64
|
end
|
|
25
65
|
|
|
26
66
|
# Validates a given request.
|
|
@@ -34,13 +74,18 @@ module Grape
|
|
|
34
74
|
validate!(request.params)
|
|
35
75
|
end
|
|
36
76
|
|
|
77
|
+
def fail_fast?
|
|
78
|
+
@fail_fast
|
|
79
|
+
end
|
|
80
|
+
|
|
37
81
|
# Validates a given parameter hash.
|
|
38
|
-
# @note Override #
|
|
82
|
+
# @note Override #validate_param! for per-parameter validation,
|
|
83
|
+
# or #validate if you need access to the entire request.
|
|
39
84
|
# @param params [Hash] parameters to validate
|
|
40
85
|
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
41
86
|
# @return [void]
|
|
42
87
|
def validate!(params)
|
|
43
|
-
attributes = SingleAttributeIterator.new(
|
|
88
|
+
attributes = SingleAttributeIterator.new(@attrs, @scope, params)
|
|
44
89
|
# we collect errors inside array because
|
|
45
90
|
# there may be more than one error per field
|
|
46
91
|
array_errors = []
|
|
@@ -49,7 +94,7 @@ module Grape
|
|
|
49
94
|
next if !@scope.required? && empty_val
|
|
50
95
|
next unless @scope.meets_dependency?(val, params)
|
|
51
96
|
|
|
52
|
-
validate_param!(attr_name, val) if @required || val.
|
|
97
|
+
validate_param!(attr_name, val) if @required || (hash_like?(val) && val.key?(attr_name))
|
|
53
98
|
rescue Grape::Exceptions::Validation => e
|
|
54
99
|
array_errors << e
|
|
55
100
|
end
|
|
@@ -57,32 +102,57 @@ module Grape
|
|
|
57
102
|
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
|
|
58
103
|
end
|
|
59
104
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
105
|
+
protected
|
|
106
|
+
|
|
107
|
+
# Validates a single attribute. Override in subclasses.
|
|
108
|
+
# @param attr_name [Symbol, String] the attribute name
|
|
109
|
+
# @param params [Hash] the parameter hash containing the attribute
|
|
110
|
+
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
111
|
+
# @return [void]
|
|
112
|
+
def validate_param!(attr_name, params)
|
|
113
|
+
raise NotImplementedError
|
|
63
114
|
end
|
|
64
115
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def validation_error!(attr_name_or_params, message = @exception_message)
|
|
119
|
+
params = attr_name_or_params.is_a?(Array) ? attr_name_or_params : @scope.full_name(attr_name_or_params)
|
|
120
|
+
raise Grape::Exceptions::Validation.new(params:, message:)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def hash_like?(obj)
|
|
124
|
+
obj.respond_to?(:key?)
|
|
68
125
|
end
|
|
69
126
|
|
|
70
127
|
def options_key?(key, options = nil)
|
|
71
|
-
|
|
72
|
-
|
|
128
|
+
current_options = options || @options
|
|
129
|
+
hash_like?(current_options) && current_options.key?(key) && !current_options[key].nil?
|
|
73
130
|
end
|
|
74
131
|
|
|
75
|
-
|
|
76
|
-
|
|
132
|
+
# Returns the effective message for a validation error.
|
|
133
|
+
# Prefers an explicit +:message+ option, then +default_key+.
|
|
134
|
+
# If both are nil, the block (if given) is called to compute a fallback —
|
|
135
|
+
# useful for validators that build a message Hash for deferred i18n interpolation.
|
|
136
|
+
# @example
|
|
137
|
+
# @exception_message = message(:presence) # symbol key or custom message
|
|
138
|
+
# @exception_message = message { build_hash_message } # computed fallback
|
|
139
|
+
def message(default_key = nil)
|
|
140
|
+
key = options_key?(:message) ? @options[:message] : default_key
|
|
141
|
+
return key unless key.nil?
|
|
142
|
+
|
|
143
|
+
yield if block_given?
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def option_value
|
|
147
|
+
options_key?(:value) ? @options[:value] : @options
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def scrub(value)
|
|
151
|
+
return value unless value.respond_to?(:valid_encoding?) && !value.valid_encoding?
|
|
152
|
+
|
|
153
|
+
value.scrub
|
|
77
154
|
end
|
|
78
155
|
end
|
|
79
156
|
end
|
|
80
157
|
end
|
|
81
158
|
end
|
|
82
|
-
|
|
83
|
-
Grape::Validations::Base = Class.new(Grape::Validations::Validators::Base) do
|
|
84
|
-
def self.inherited(*)
|
|
85
|
-
Grape.deprecator.warn 'Grape::Validations::Base is deprecated! Use Grape::Validations::Validators::Base instead.'
|
|
86
|
-
super
|
|
87
|
-
end
|
|
88
|
-
end
|
|
@@ -4,22 +4,27 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class CoerceValidator < Base
|
|
7
|
+
default_message_key :coerce
|
|
8
|
+
|
|
7
9
|
def initialize(attrs, options, required, scope, opts)
|
|
8
10
|
super
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
raw_type = @options[:type]
|
|
13
|
+
type = hash_like?(raw_type) ? raw_type[:value] : raw_type
|
|
14
|
+
@converter =
|
|
15
|
+
if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
|
|
16
|
+
type
|
|
17
|
+
else
|
|
18
|
+
Types.build_coercer(type, method: @options[:method])
|
|
19
|
+
end
|
|
15
20
|
end
|
|
16
21
|
|
|
17
22
|
def validate_param!(attr_name, params)
|
|
18
|
-
|
|
23
|
+
validation_error!(attr_name) unless hash_like?(params)
|
|
19
24
|
|
|
20
25
|
new_value = coerce_value(params[attr_name])
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
validation_error!(attr_name, new_value.message || @exception_message) if new_value.is_a?(Types::InvalidValue)
|
|
23
28
|
|
|
24
29
|
# Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
|
|
25
30
|
# which looses wrappers for hashes and arrays after reassigning values
|
|
@@ -37,38 +42,13 @@ module Grape
|
|
|
37
42
|
|
|
38
43
|
private
|
|
39
44
|
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
# See {Types.build_coercer}
|
|
44
|
-
#
|
|
45
|
-
# @return [Object]
|
|
46
|
-
attr_reader :converter
|
|
47
|
-
|
|
48
|
-
def valid_type?(val)
|
|
49
|
-
!val.is_a?(Types::InvalidValue)
|
|
50
|
-
end
|
|
51
|
-
|
|
45
|
+
# Calls the converter built at definition time.
|
|
46
|
+
# Custom coercers may raise; any StandardError is treated as an invalid value.
|
|
52
47
|
def coerce_value(val)
|
|
53
|
-
converter.call(val)
|
|
54
|
-
# Some custom types might fail, so it should be treated as an invalid value
|
|
48
|
+
@converter.call(val)
|
|
55
49
|
rescue StandardError
|
|
56
50
|
Types::InvalidValue.new
|
|
57
51
|
end
|
|
58
|
-
|
|
59
|
-
# Type to which the parameter will be coerced.
|
|
60
|
-
#
|
|
61
|
-
# @return [Class]
|
|
62
|
-
def type
|
|
63
|
-
@option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def validation_exception(attr_name, custom_msg = nil)
|
|
67
|
-
Grape::Exceptions::Validation.new(
|
|
68
|
-
params: [@scope.full_name(attr_name)],
|
|
69
|
-
message: custom_msg || message(:coerce)
|
|
70
|
-
)
|
|
71
|
-
end
|
|
72
52
|
end
|
|
73
53
|
end
|
|
74
54
|
end
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
|
-
class ContractScopeValidator
|
|
6
|
+
class ContractScopeValidator
|
|
7
7
|
attr_reader :schema
|
|
8
8
|
|
|
9
|
-
def initialize(
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
def initialize(schema:)
|
|
10
|
+
@schema = schema
|
|
11
|
+
freeze
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# Validates a given request.
|
|
@@ -26,13 +26,17 @@ module Grape
|
|
|
26
26
|
raise Grape::Exceptions::ValidationArrayErrors.new(build_errors_from_messages(res.errors.messages))
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def fail_fast?
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
29
33
|
private
|
|
30
34
|
|
|
31
35
|
def build_errors_from_messages(messages)
|
|
32
36
|
messages.map do |message|
|
|
33
37
|
full_name = message.path.first.to_s
|
|
34
38
|
full_name << "[#{message.path[1..].join('][')}]" if message.path.size > 1
|
|
35
|
-
Grape::Exceptions::Validation.new(params:
|
|
39
|
+
Grape::Exceptions::Validation.new(params: full_name, message: message.text)
|
|
36
40
|
end
|
|
37
41
|
end
|
|
38
42
|
end
|
|
@@ -4,31 +4,25 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class DefaultValidator < Base
|
|
7
|
-
def initialize(attrs, options, required, scope, opts
|
|
8
|
-
@default = options
|
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
|
9
8
|
super
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
elsif @default.frozen? || !@default.duplicable?
|
|
20
|
-
@default
|
|
21
|
-
else
|
|
22
|
-
@default.dup
|
|
23
|
-
end
|
|
9
|
+
# !important, lazy call at runtime
|
|
10
|
+
@default_call =
|
|
11
|
+
if @options.is_a?(Proc)
|
|
12
|
+
@options.arity.zero? ? proc { @options.call } : @options
|
|
13
|
+
elsif @options.duplicable?
|
|
14
|
+
proc { @options.dup }
|
|
15
|
+
else
|
|
16
|
+
proc { @options }
|
|
17
|
+
end
|
|
24
18
|
end
|
|
25
19
|
|
|
26
20
|
def validate!(params)
|
|
27
|
-
attrs = SingleAttributeIterator.new(
|
|
21
|
+
attrs = SingleAttributeIterator.new(@attrs, @scope, params)
|
|
28
22
|
attrs.each do |resource_params, attr_name|
|
|
29
23
|
next unless @scope.meets_dependency?(resource_params, params)
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
resource_params[attr_name] = @default_call.call(resource_params) if hash_like?(resource_params) && resource_params[attr_name].nil?
|
|
32
26
|
end
|
|
33
27
|
end
|
|
34
28
|
end
|
|
@@ -4,12 +4,19 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class ExactlyOneOfValidator < MultipleParamsBase
|
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
|
8
|
+
super
|
|
9
|
+
@exactly_one_exception_message = message(:exactly_one)
|
|
10
|
+
@mutual_exclusion_exception_message = message(:mutual_exclusion)
|
|
11
|
+
end
|
|
12
|
+
|
|
7
13
|
def validate_params!(params)
|
|
8
|
-
|
|
14
|
+
known_keys = all_keys
|
|
15
|
+
keys = keys_in_common(params, known_keys)
|
|
9
16
|
return if keys.length == 1
|
|
10
|
-
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.empty?
|
|
11
17
|
|
|
12
|
-
|
|
18
|
+
validation_error!(known_keys, @exactly_one_exception_message) if keys.empty?
|
|
19
|
+
validation_error!(keys, @mutual_exclusion_exception_message)
|
|
13
20
|
end
|
|
14
21
|
end
|
|
15
22
|
end
|
|
@@ -4,19 +4,28 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class ExceptValuesValidator < Base
|
|
7
|
+
default_message_key :except_values
|
|
8
|
+
|
|
7
9
|
def initialize(attrs, options, required, scope, opts)
|
|
8
|
-
@except = options.is_a?(Hash) ? options[:value] : options
|
|
9
10
|
super
|
|
11
|
+
except = option_value
|
|
12
|
+
raise ArgumentError, 'except_values Proc must have arity of zero (use values: with a one-arity predicate for per-element checks)' if except.is_a?(Proc) && !except.arity.zero?
|
|
13
|
+
|
|
14
|
+
# Zero-arity procs (e.g. -> { User.pluck(:role) }) must be called per-request,
|
|
15
|
+
# not at definition time, so they are wrapped in a lambda to defer execution.
|
|
16
|
+
@excepts_call = except.is_a?(Proc) ? except : -> { except }
|
|
10
17
|
end
|
|
11
18
|
|
|
12
19
|
def validate_param!(attr_name, params)
|
|
13
|
-
return unless params.
|
|
20
|
+
return unless hash_like?(params) && params.key?(attr_name)
|
|
14
21
|
|
|
15
|
-
excepts = @
|
|
22
|
+
excepts = @excepts_call.call
|
|
16
23
|
return if excepts.nil?
|
|
17
24
|
|
|
18
25
|
param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
|
|
19
|
-
|
|
26
|
+
return if param_array.none? { |param| excepts.include?(param) }
|
|
27
|
+
|
|
28
|
+
validation_error!(attr_name)
|
|
20
29
|
end
|
|
21
30
|
end
|
|
22
31
|
end
|
|
@@ -5,19 +5,16 @@ module Grape
|
|
|
5
5
|
module Validators
|
|
6
6
|
class LengthValidator < Base
|
|
7
7
|
def initialize(attrs, options, required, scope, opts)
|
|
8
|
-
@min = options[:min]
|
|
9
|
-
@max = options[:max]
|
|
10
|
-
@is = options[:is]
|
|
11
|
-
|
|
12
8
|
super
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
@min, @max, @is = @options.values_at(:min, :max, :is)
|
|
11
|
+
validate_boundary!(:min, @min)
|
|
12
|
+
validate_boundary!(:max, @max)
|
|
13
|
+
raise ArgumentError, "min #{@min} cannot be greater than max #{@max}" if @min && @max && @min > @max
|
|
17
14
|
|
|
18
15
|
return if @is.nil?
|
|
19
|
-
raise ArgumentError, 'is must be an integer greater than zero'
|
|
20
|
-
raise ArgumentError, 'is cannot be combined with min or max'
|
|
16
|
+
raise ArgumentError, 'is must be an integer greater than zero' unless @is.is_a?(Integer) && @is.positive?
|
|
17
|
+
raise ArgumentError, 'is cannot be combined with min or max' unless @min.nil? && @max.nil?
|
|
21
18
|
end
|
|
22
19
|
|
|
23
20
|
def validate_param!(attr_name, params)
|
|
@@ -27,21 +24,23 @@ module Grape
|
|
|
27
24
|
|
|
28
25
|
return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max) || (!@is.nil? && param.length != @is)
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
validation_error!(attr_name, message do
|
|
28
|
+
if @min && @max
|
|
29
|
+
translate(:length, min: @min, max: @max)
|
|
30
|
+
elsif @min
|
|
31
|
+
translate(:length_min, min: @min)
|
|
32
|
+
elsif @max
|
|
33
|
+
translate(:length_max, max: @max)
|
|
34
|
+
else
|
|
35
|
+
translate(:length_is, is: @is)
|
|
36
|
+
end
|
|
37
|
+
end)
|
|
31
38
|
end
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
format I18n.t(:length, scope: 'grape.errors.messages'), min: @min, max: @max
|
|
38
|
-
elsif @min
|
|
39
|
-
format I18n.t(:length_min, scope: 'grape.errors.messages'), min: @min
|
|
40
|
-
elsif @max
|
|
41
|
-
format I18n.t(:length_max, scope: 'grape.errors.messages'), max: @max
|
|
42
|
-
else
|
|
43
|
-
format I18n.t(:length_is, scope: 'grape.errors.messages'), is: @is
|
|
44
|
-
end
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def validate_boundary!(name, val)
|
|
43
|
+
raise ArgumentError, "#{name} must be an integer greater than or equal to zero" if !val.nil? && (!val.is_a?(Integer) || val.negative?)
|
|
45
44
|
end
|
|
46
45
|
end
|
|
47
46
|
end
|
|
@@ -5,7 +5,7 @@ module Grape
|
|
|
5
5
|
module Validators
|
|
6
6
|
class MultipleParamsBase < Base
|
|
7
7
|
def validate!(params)
|
|
8
|
-
attributes = MultipleAttributesIterator.new(
|
|
8
|
+
attributes = MultipleAttributesIterator.new(@attrs, @scope, params)
|
|
9
9
|
array_errors = []
|
|
10
10
|
|
|
11
11
|
attributes.each do |resource_params|
|
|
@@ -19,14 +19,14 @@ module Grape
|
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
|
-
def keys_in_common(resource_params)
|
|
23
|
-
return [] unless
|
|
22
|
+
def keys_in_common(resource_params, known_keys = all_keys)
|
|
23
|
+
return [] unless hash_like?(resource_params)
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
known_keys & resource_params.keys.map! { |attr| @scope.full_name(attr) }
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def all_keys
|
|
29
|
-
attrs.map { |attr| @scope.full_name(attr) }
|
|
29
|
+
@attrs.map { |attr| @scope.full_name(attr) }
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
end
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
|
-
class
|
|
6
|
+
class MutuallyExclusiveValidator < MultipleParamsBase
|
|
7
|
+
default_message_key :mutual_exclusion
|
|
8
|
+
|
|
7
9
|
def validate_params!(params)
|
|
8
10
|
keys = keys_in_common(params)
|
|
9
11
|
return if keys.length <= 1
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
validation_error!(keys)
|
|
12
14
|
end
|
|
13
15
|
end
|
|
14
16
|
end
|
|
@@ -4,10 +4,12 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class PresenceValidator < Base
|
|
7
|
+
default_message_key :presence
|
|
8
|
+
|
|
7
9
|
def validate_param!(attr_name, params)
|
|
8
|
-
return if params.
|
|
10
|
+
return if hash_like?(params) && params.key?(attr_name)
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
validation_error!(attr_name)
|
|
11
13
|
end
|
|
12
14
|
end
|
|
13
15
|
end
|
|
@@ -4,11 +4,19 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class RegexpValidator < Base
|
|
7
|
+
default_message_key :regexp
|
|
8
|
+
|
|
9
|
+
def initialize(attrs, options, required, scope, opts)
|
|
10
|
+
super
|
|
11
|
+
@value = option_value
|
|
12
|
+
end
|
|
13
|
+
|
|
7
14
|
def validate_param!(attr_name, params)
|
|
8
|
-
return unless params.
|
|
9
|
-
|
|
15
|
+
return unless hash_like?(params) && params.key?(attr_name)
|
|
16
|
+
|
|
17
|
+
return if Array.wrap(params[attr_name]).all? { |param| param.nil? || scrub(param.to_s).match?(@value) }
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
validation_error!(attr_name)
|
|
12
20
|
end
|
|
13
21
|
end
|
|
14
22
|
end
|