grape 2.0.0 → 2.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 +151 -1
- data/CONTRIBUTING.md +1 -1
- data/README.md +404 -334
- data/UPGRADING.md +279 -7
- data/grape.gemspec +8 -8
- data/lib/grape/api/instance.rb +34 -66
- data/lib/grape/api.rb +47 -70
- data/lib/grape/content_types.rb +13 -10
- data/lib/grape/cookies.rb +31 -24
- data/lib/grape/dry_types.rb +0 -2
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/desc.rb +49 -44
- data/lib/grape/dsl/headers.rb +2 -2
- data/lib/grape/dsl/helpers.rb +8 -4
- data/lib/grape/dsl/inside_route.rb +67 -54
- data/lib/grape/dsl/parameters.rb +10 -9
- data/lib/grape/dsl/request_response.rb +14 -18
- data/lib/grape/dsl/routing.rb +34 -17
- data/lib/grape/dsl/validations.rb +13 -0
- data/lib/grape/endpoint.rb +120 -118
- data/lib/grape/{util/env.rb → env.rb} +0 -5
- data/lib/grape/error_formatter/base.rb +51 -21
- data/lib/grape/error_formatter/json.rb +7 -15
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +11 -17
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +5 -25
- data/lib/grape/exceptions/base.rb +18 -30
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/validation.rb +5 -6
- data/lib/grape/exceptions/validation_array_errors.rb +1 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -6
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
- data/lib/grape/extensions/hash.rb +7 -2
- data/lib/grape/extensions/hashie/mash.rb +3 -5
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +4 -6
- data/lib/grape/formatter/serializable_hash.rb +1 -1
- data/lib/grape/formatter/txt.rb +3 -5
- data/lib/grape/formatter/xml.rb +4 -6
- data/lib/grape/formatter.rb +7 -25
- data/lib/grape/{util/json.rb → json.rb} +1 -3
- data/lib/grape/locale/en.yml +46 -42
- data/lib/grape/middleware/auth/base.rb +11 -34
- data/lib/grape/middleware/auth/dsl.rb +23 -31
- data/lib/grape/middleware/base.rb +41 -23
- data/lib/grape/middleware/error.rb +77 -76
- data/lib/grape/middleware/formatter.rb +48 -79
- data/lib/grape/middleware/globals.rb +1 -3
- data/lib/grape/middleware/stack.rb +26 -37
- data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
- data/lib/grape/middleware/versioner/base.rb +74 -0
- data/lib/grape/middleware/versioner/header.rb +59 -126
- data/lib/grape/middleware/versioner/param.rb +4 -25
- data/lib/grape/middleware/versioner/path.rb +10 -34
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/namespace.rb +4 -5
- data/lib/grape/params_builder/base.rb +18 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -23
- data/lib/grape/path.rb +38 -60
- data/lib/grape/request.rb +161 -30
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +45 -31
- data/lib/grape/router/route.rb +28 -57
- data/lib/grape/router.rb +56 -43
- data/lib/grape/util/base_inheritable.rb +4 -4
- data/lib/grape/util/cache.rb +0 -3
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_values.rb +0 -2
- data/lib/grape/util/lazy/block.rb +29 -0
- data/lib/grape/util/lazy/value.rb +38 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +34 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +70 -0
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/util/reverse_stackable_values.rb +1 -6
- data/lib/grape/util/stackable_values.rb +1 -6
- data/lib/grape/util/strict_hash_configuration.rb +3 -3
- data/lib/grape/validations/attributes_doc.rb +38 -36
- data/lib/grape/validations/attributes_iterator.rb +1 -0
- data/lib/grape/validations/contract_scope.rb +34 -0
- data/lib/grape/validations/params_scope.rb +36 -32
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +9 -15
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +0 -2
- data/lib/grape/validations/types/set_coercer.rb +0 -3
- data/lib/grape/validations/types.rb +0 -3
- data/lib/grape/validations/validator_factory.rb +2 -2
- data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
- data/lib/grape/validations/validators/base.rb +8 -11
- data/lib/grape/validations/validators/coerce_validator.rb +1 -1
- data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
- data/lib/grape/validations/validators/default_validator.rb +6 -2
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +1 -1
- data/lib/grape/validations/validators/except_values_validator.rb +2 -2
- data/lib/grape/validations/validators/length_validator.rb +49 -0
- data/lib/grape/validations/validators/presence_validator.rb +1 -1
- data/lib/grape/validations/validators/regexp_validator.rb +2 -2
- data/lib/grape/validations/validators/values_validator.rb +20 -57
- data/lib/grape/validations.rb +8 -21
- data/lib/grape/version.rb +1 -1
- data/lib/grape/{util/xml.rb → xml.rb} +1 -1
- data/lib/grape.rb +42 -274
- metadata +45 -44
- data/lib/grape/eager_load.rb +0 -20
- data/lib/grape/http/headers.rb +0 -71
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
- data/lib/grape/router/attribute_translator.rb +0 -63
- data/lib/grape/util/lazy_block.rb +0 -27
- data/lib/grape/util/lazy_object.rb +0 -43
- data/lib/grape/util/lazy_value.rb +0 -91
- data/lib/grape/util/registrable.rb +0 -15
- data/lib/grape/validations/types/build_coercer.rb +0 -94
@@ -2,56 +2,58 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
5
|
+
# Documents parameters of an endpoint. If documentation isn't needed (for instance, it is an
|
6
|
+
# internal API), the class only cleans up attributes to avoid junk in RAM.
|
7
|
+
|
8
|
+
class AttributesDoc
|
9
|
+
attr_accessor :type, :values
|
10
|
+
|
11
|
+
# @param api [Grape::API::Instance]
|
12
|
+
# @param scope [Validations::ParamsScope]
|
13
|
+
def initialize(api, scope)
|
14
|
+
@api = api
|
15
|
+
@scope = scope
|
16
|
+
@type = type
|
17
|
+
end
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
def extract_details(validations)
|
20
|
+
details[:required] = validations.key?(:presence)
|
21
21
|
|
22
|
-
|
22
|
+
desc = validations.delete(:desc) || validations.delete(:description)
|
23
23
|
|
24
|
-
|
24
|
+
details[:desc] = desc if desc
|
25
25
|
|
26
|
-
|
26
|
+
documentation = validations.delete(:documentation)
|
27
27
|
|
28
|
-
|
28
|
+
details[:documentation] = documentation if documentation
|
29
29
|
|
30
|
-
|
31
|
-
end
|
30
|
+
details[:default] = validations[:default] if validations.key?(:default)
|
32
31
|
|
33
|
-
|
34
|
-
|
32
|
+
details[:min_length] = validations[:length][:min] if validations.key?(:length) && validations[:length].key?(:min)
|
33
|
+
details[:max_length] = validations[:length][:max] if validations.key?(:length) && validations[:length].key?(:max)
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
def document(attrs)
|
37
|
+
return if @api.namespace_inheritable(:do_not_document)
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
end
|
39
|
+
details[:type] = type.to_s if type
|
40
|
+
details[:values] = values if values
|
42
41
|
|
43
|
-
|
42
|
+
documented_attrs = attrs.each_with_object({}) do |name, memo|
|
43
|
+
memo[@scope.full_name(name)] = details
|
44
44
|
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
@api.namespace_stackable(:params, documented_attrs)
|
47
|
+
end
|
48
|
+
|
49
|
+
def required
|
50
|
+
details[:required]
|
51
|
+
end
|
49
52
|
|
50
|
-
|
53
|
+
protected
|
51
54
|
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
+
def details
|
56
|
+
@details ||= {}
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
@@ -21,6 +21,7 @@ module Grape
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def do_each(params_to_process, parent_indicies = [], &block)
|
24
|
+
@scope.reset_index # gets updated depending on the size of params_to_process
|
24
25
|
params_to_process.each_with_index do |resource_params, index|
|
25
26
|
# when we get arrays of arrays it means that target element located inside array
|
26
27
|
# we need this because we want to know parent arrays indicies
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
class ContractScope
|
6
|
+
# Declare the contract to be used for the endpoint's parameters.
|
7
|
+
# @param api [API] the API endpoint to modify.
|
8
|
+
# @param contract the contract or schema to be used for validation. Optional.
|
9
|
+
# @yield a block yielding a new schema class. Optional.
|
10
|
+
def initialize(api, contract = nil, &block)
|
11
|
+
# When block is passed, the first arg is either schema or nil.
|
12
|
+
contract = Dry::Schema.Params(parent: contract, &block) if block
|
13
|
+
|
14
|
+
if contract.respond_to?(:schema)
|
15
|
+
# It's a Dry::Validation::Contract, then.
|
16
|
+
contract = contract.new
|
17
|
+
key_map = contract.schema.key_map
|
18
|
+
else
|
19
|
+
# Dry::Schema::Processor, hopefully.
|
20
|
+
key_map = contract.key_map
|
21
|
+
end
|
22
|
+
|
23
|
+
api.namespace_stackable(:contract_key_map, key_map)
|
24
|
+
|
25
|
+
validator_options = {
|
26
|
+
validator_class: Grape::Validations.require_validator(:contract_scope),
|
27
|
+
opts: { schema: contract, fail_fast: false }
|
28
|
+
}
|
29
|
+
|
30
|
+
api.namespace_stackable(:validations, validator_options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'attributes_doc'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module Validations
|
7
5
|
class ParamsScope
|
8
6
|
attr_accessor :element, :parent, :index
|
9
|
-
attr_reader :type
|
7
|
+
attr_reader :type, :params_meeting_dependency
|
10
8
|
|
11
9
|
include Grape::DSL::Parameters
|
12
10
|
|
@@ -69,6 +67,7 @@ module Grape
|
|
69
67
|
@type = opts[:type]
|
70
68
|
@group = opts[:group]
|
71
69
|
@dependent_on = opts[:dependent_on]
|
70
|
+
@params_meeting_dependency = []
|
72
71
|
@declared_params = []
|
73
72
|
@index = nil
|
74
73
|
|
@@ -95,17 +94,18 @@ module Grape
|
|
95
94
|
|
96
95
|
def meets_dependency?(params, request_params)
|
97
96
|
return true unless @dependent_on
|
98
|
-
|
99
97
|
return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
|
100
98
|
|
101
|
-
|
99
|
+
if params.is_a?(Array)
|
100
|
+
@params_meeting_dependency = params.flatten.filter { |param| meets_dependency?(param, request_params) }
|
101
|
+
return @params_meeting_dependency.present?
|
102
|
+
end
|
102
103
|
|
103
104
|
meets_hash_dependency?(params)
|
104
105
|
end
|
105
106
|
|
106
107
|
def attr_meets_dependency?(params)
|
107
108
|
return true unless @dependent_on
|
108
|
-
|
109
109
|
return false if @parent.present? && !@parent.attr_meets_dependency?(params)
|
110
110
|
|
111
111
|
meets_hash_dependency?(params)
|
@@ -132,7 +132,7 @@ module Grape
|
|
132
132
|
def full_name(name, index: nil)
|
133
133
|
if nested?
|
134
134
|
# Find our containing element's name, and append ours.
|
135
|
-
"#{@parent.full_name(@element)}#{brackets(
|
135
|
+
"#{@parent.full_name(@element)}#{brackets(index || @index)}#{brackets(name)}"
|
136
136
|
elsif lateral?
|
137
137
|
# Find the name of the element as if it was at the same nesting level
|
138
138
|
# as our parent. We need to forward our index upward to achieve this.
|
@@ -171,27 +171,33 @@ module Grape
|
|
171
171
|
!@optional
|
172
172
|
end
|
173
173
|
|
174
|
+
def reset_index
|
175
|
+
@index = nil
|
176
|
+
end
|
177
|
+
|
174
178
|
protected
|
175
179
|
|
176
180
|
# Adds a parameter declaration to our list of validations.
|
177
181
|
# @param attrs [Array] (see Grape::DSL::Parameters#requires)
|
178
|
-
def push_declared_params(attrs,
|
179
|
-
opts =
|
180
|
-
if lateral?
|
181
|
-
@parent.push_declared_params(attrs, **opts)
|
182
|
-
else
|
183
|
-
push_renamed_param(full_path + [attrs.first], opts[:as]) \
|
184
|
-
if opts && opts[:as]
|
182
|
+
def push_declared_params(attrs, opts = {})
|
183
|
+
opts[:declared_params_scope] = self unless opts.key?(:declared_params_scope)
|
184
|
+
return @parent.push_declared_params(attrs, opts) if lateral?
|
185
185
|
|
186
|
-
|
187
|
-
|
186
|
+
push_renamed_param(full_path + [attrs.first], opts[:as]) if opts[:as]
|
187
|
+
@declared_params.concat(attrs.map { |attr| ::Grape::Validations::ParamsScope::Attr.new(attr, opts[:declared_params_scope]) })
|
188
188
|
end
|
189
189
|
|
190
190
|
# Get the full path of the parameter scope in the hierarchy.
|
191
191
|
#
|
192
192
|
# @return [Array<Symbol>] the nesting/path of the current parameter scope
|
193
193
|
def full_path
|
194
|
-
nested?
|
194
|
+
if nested?
|
195
|
+
(@parent.full_path + [@element])
|
196
|
+
elsif lateral?
|
197
|
+
@parent.full_path
|
198
|
+
else
|
199
|
+
[]
|
200
|
+
end
|
195
201
|
end
|
196
202
|
|
197
203
|
private
|
@@ -211,11 +217,11 @@ module Grape
|
|
211
217
|
|
212
218
|
def require_required_and_optional_fields(context, opts)
|
213
219
|
if context == :all
|
214
|
-
optional_fields = Array(opts[:except])
|
215
|
-
required_fields = opts[:using].keys
|
220
|
+
optional_fields = Array.wrap(opts[:except])
|
221
|
+
required_fields = opts[:using].keys.delete_if { |f| optional_fields.include?(f) }
|
216
222
|
else # context == :none
|
217
|
-
required_fields = Array(opts[:except])
|
218
|
-
optional_fields = opts[:using].keys
|
223
|
+
required_fields = Array.wrap(opts[:except])
|
224
|
+
optional_fields = opts[:using].keys.delete_if { |f| required_fields.include?(f) }
|
219
225
|
end
|
220
226
|
required_fields.each do |field|
|
221
227
|
field_opts = opts[:using][field]
|
@@ -231,7 +237,10 @@ module Grape
|
|
231
237
|
|
232
238
|
def require_optional_fields(context, opts)
|
233
239
|
optional_fields = opts[:using].keys
|
234
|
-
|
240
|
+
unless context == :all
|
241
|
+
except_fields = Array.wrap(opts[:except])
|
242
|
+
optional_fields.delete_if { |f| except_fields.include?(f) }
|
243
|
+
end
|
235
244
|
optional_fields.each do |field|
|
236
245
|
field_opts = opts[:using][field]
|
237
246
|
optional(field, field_opts) if field_opts
|
@@ -266,6 +275,7 @@ module Grape
|
|
266
275
|
parent: self,
|
267
276
|
optional: optional,
|
268
277
|
type: type || Array,
|
278
|
+
group: @group,
|
269
279
|
&block
|
270
280
|
)
|
271
281
|
end
|
@@ -295,12 +305,7 @@ module Grape
|
|
295
305
|
# `optional` invocation that opened this scope.
|
296
306
|
# @yield parameter scope
|
297
307
|
def new_group_scope(attrs, &block)
|
298
|
-
self.class.new(
|
299
|
-
api: @api,
|
300
|
-
parent: self,
|
301
|
-
group: attrs.first,
|
302
|
-
&block
|
303
|
-
)
|
308
|
+
self.class.new(api: @api, parent: self, group: attrs.first, &block)
|
304
309
|
end
|
305
310
|
|
306
311
|
# Pushes declared params to parent or settings
|
@@ -463,8 +468,7 @@ module Grape
|
|
463
468
|
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) if values && !values.is_a?(Proc) && !Array(default).all? { |def_val| values.include?(def_val) }
|
464
469
|
|
465
470
|
if except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }
|
466
|
-
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
|
467
|
-
|
471
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
|
468
472
|
end
|
469
473
|
|
470
474
|
return unless excepts && !excepts.is_a?(Proc)
|
@@ -487,7 +491,7 @@ module Grape
|
|
487
491
|
def validate_value_coercion(coerce_type, *values_list)
|
488
492
|
return unless coerce_type
|
489
493
|
|
490
|
-
coerce_type = coerce_type.first if coerce_type.is_a?(
|
494
|
+
coerce_type = coerce_type.first if coerce_type.is_a?(Enumerable)
|
491
495
|
values_list.each do |values|
|
492
496
|
next if !values || values.is_a?(Proc)
|
493
497
|
|
@@ -526,7 +530,7 @@ module Grape
|
|
526
530
|
def validates_presence(validations, attrs, doc, opts)
|
527
531
|
return unless validations.key?(:presence) && validations[:presence]
|
528
532
|
|
529
|
-
validate(
|
533
|
+
validate('presence', validations.delete(:presence), attrs, doc, opts)
|
530
534
|
validations.delete(:message) if validations.key?(:message)
|
531
535
|
end
|
532
536
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry-types'
|
4
|
-
|
5
3
|
module DryTypes
|
6
4
|
# Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
|
7
5
|
# a container in this case. Check documentation for more information
|
@@ -24,24 +22,20 @@ module Grape
|
|
24
22
|
# collection_coercer_for(Array)
|
25
23
|
# #=> Grape::Validations::Types::ArrayCoercer
|
26
24
|
def collection_coercer_for(type)
|
27
|
-
|
28
|
-
|
25
|
+
case type
|
26
|
+
when Array
|
27
|
+
ArrayCoercer
|
28
|
+
when Set
|
29
|
+
SetCoercer
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Unknown type: #{type}"
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
32
35
|
# Returns an instance of a coercer for a given type
|
33
36
|
def coercer_instance_for(type, strict = false)
|
34
|
-
|
35
|
-
|
36
|
-
# in case of a collection (Array[Integer]) the type is an instance of a collection,
|
37
|
-
# so we need to figure out the actual type
|
38
|
-
collection_coercer_for(type.class).new(type, strict)
|
39
|
-
end
|
40
|
-
|
41
|
-
protected
|
42
|
-
|
43
|
-
def collection_coercers
|
44
|
-
@collection_coercers ||= {}
|
37
|
+
klass = type.instance_of?(Class) ? PrimitiveCoercer : collection_coercer_for(type)
|
38
|
+
klass.new(type, strict)
|
45
39
|
end
|
46
40
|
end
|
47
41
|
|
@@ -3,12 +3,12 @@
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
5
|
class ValidatorFactory
|
6
|
-
def self.create_validator(
|
6
|
+
def self.create_validator(options)
|
7
7
|
options[:validator_class].new(options[:attributes],
|
8
8
|
options[:options],
|
9
9
|
options[:required],
|
10
10
|
options[:params_scope],
|
11
|
-
|
11
|
+
options[:opts])
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -8,7 +8,7 @@ module Grape
|
|
8
8
|
return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)
|
9
9
|
|
10
10
|
value = params[attr_name]
|
11
|
-
value = value.
|
11
|
+
value = value.scrub if value.respond_to?(:scrub)
|
12
12
|
|
13
13
|
return if value == false || value.present?
|
14
14
|
|
@@ -13,15 +13,14 @@ module Grape
|
|
13
13
|
# @param options [Object] implementation-dependent Validator options
|
14
14
|
# @param required [Boolean] attribute(s) are required or optional
|
15
15
|
# @param scope [ParamsScope] parent scope for this Validator
|
16
|
-
# @param opts [
|
17
|
-
def initialize(attrs, options, required, scope,
|
16
|
+
# @param opts [Hash] additional validation options
|
17
|
+
def initialize(attrs, options, required, scope, opts)
|
18
18
|
@attrs = Array(attrs)
|
19
19
|
@option = options
|
20
20
|
@required = required
|
21
21
|
@scope = scope
|
22
|
-
|
23
|
-
@
|
24
|
-
@allow_blank = opts.fetch(:allow_blank, false)
|
22
|
+
@fail_fast = opts[:fail_fast]
|
23
|
+
@allow_blank = opts[:allow_blank]
|
25
24
|
end
|
26
25
|
|
27
26
|
# Validates a given request.
|
@@ -50,7 +49,7 @@ module Grape
|
|
50
49
|
next if !@scope.required? && empty_val
|
51
50
|
next unless @scope.meets_dependency?(val, params)
|
52
51
|
|
53
|
-
validate_param!(attr_name, val) if @required ||
|
52
|
+
validate_param!(attr_name, val) if @required || val.try(:key?, attr_name)
|
54
53
|
rescue Grape::Exceptions::Validation => e
|
55
54
|
array_errors << e
|
56
55
|
end
|
@@ -59,10 +58,8 @@ module Grape
|
|
59
58
|
end
|
60
59
|
|
61
60
|
def self.inherited(klass)
|
62
|
-
|
63
|
-
|
64
|
-
short_validator_name = klass.name.demodulize.underscore.delete_suffix('_validator')
|
65
|
-
Validations.register_validator(short_validator_name, klass)
|
61
|
+
super
|
62
|
+
Validations.register(klass)
|
66
63
|
end
|
67
64
|
|
68
65
|
def message(default_key = nil)
|
@@ -72,7 +69,7 @@ module Grape
|
|
72
69
|
|
73
70
|
def options_key?(key, options = nil)
|
74
71
|
options = instance_variable_get(:@option) if options.nil?
|
75
|
-
options.
|
72
|
+
options.try(:key?, key) && !options[key].nil?
|
76
73
|
end
|
77
74
|
|
78
75
|
def fail_fast?
|
@@ -4,7 +4,7 @@ module Grape
|
|
4
4
|
module Validations
|
5
5
|
module Validators
|
6
6
|
class CoerceValidator < Base
|
7
|
-
def initialize(attrs, options, required, scope,
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
8
8
|
super
|
9
9
|
|
10
10
|
@converter = if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
module Validators
|
6
|
+
class ContractScopeValidator < Base
|
7
|
+
attr_reader :schema
|
8
|
+
|
9
|
+
def initialize(_attrs, _options, _required, _scope, opts)
|
10
|
+
super
|
11
|
+
@schema = opts.fetch(:schema)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Validates a given request.
|
15
|
+
# @param request [Grape::Request] the request currently being handled
|
16
|
+
# @raise [Grape::Exceptions::ValidationArrayErrors] if validation failed
|
17
|
+
# @return [void]
|
18
|
+
def validate(request)
|
19
|
+
res = schema.call(request.params)
|
20
|
+
|
21
|
+
if res.success?
|
22
|
+
request.params.deep_merge!(res.to_h)
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
raise Grape::Exceptions::ValidationArrayErrors.new(build_errors_from_messages(res.errors.messages))
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def build_errors_from_messages(messages)
|
32
|
+
messages.map do |message|
|
33
|
+
full_name = message.path.first.to_s
|
34
|
+
full_name << "[#{message.path[1..].join('][')}]" if message.path.size > 1
|
35
|
+
Grape::Exceptions::Validation.new(params: [full_name], message: message.text)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -4,14 +4,18 @@ module Grape
|
|
4
4
|
module Validations
|
5
5
|
module Validators
|
6
6
|
class DefaultValidator < Base
|
7
|
-
def initialize(attrs, options, required, scope,
|
7
|
+
def initialize(attrs, options, required, scope, opts = {})
|
8
8
|
@default = options
|
9
9
|
super
|
10
10
|
end
|
11
11
|
|
12
12
|
def validate_param!(attr_name, params)
|
13
13
|
params[attr_name] = if @default.is_a? Proc
|
14
|
-
@default.
|
14
|
+
if @default.parameters.empty?
|
15
|
+
@default.call
|
16
|
+
else
|
17
|
+
@default.call(params)
|
18
|
+
end
|
15
19
|
elsif @default.frozen? || !@default.duplicable?
|
16
20
|
@default
|
17
21
|
else
|
@@ -7,7 +7,7 @@ module Grape
|
|
7
7
|
def validate_params!(params)
|
8
8
|
keys = keys_in_common(params)
|
9
9
|
return if keys.length == 1
|
10
|
-
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.
|
10
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.empty?
|
11
11
|
|
12
12
|
raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
|
13
13
|
end
|
@@ -4,13 +4,13 @@ module Grape
|
|
4
4
|
module Validations
|
5
5
|
module Validators
|
6
6
|
class ExceptValuesValidator < Base
|
7
|
-
def initialize(attrs, options, required, scope,
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
8
8
|
@except = options.is_a?(Hash) ? options[:value] : options
|
9
9
|
super
|
10
10
|
end
|
11
11
|
|
12
12
|
def validate_param!(attr_name, params)
|
13
|
-
return unless params.
|
13
|
+
return unless params.try(:key?, attr_name)
|
14
14
|
|
15
15
|
excepts = @except.is_a?(Proc) ? @except.call : @except
|
16
16
|
return if excepts.nil?
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
module Validators
|
6
|
+
class LengthValidator < Base
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
8
|
+
@min = options[:min]
|
9
|
+
@max = options[:max]
|
10
|
+
@is = options[:is]
|
11
|
+
|
12
|
+
super
|
13
|
+
|
14
|
+
raise ArgumentError, 'min must be an integer greater than or equal to zero' if !@min.nil? && (!@min.is_a?(Integer) || @min.negative?)
|
15
|
+
raise ArgumentError, 'max must be an integer greater than or equal to zero' if !@max.nil? && (!@max.is_a?(Integer) || @max.negative?)
|
16
|
+
raise ArgumentError, "min #{@min} cannot be greater than max #{@max}" if !@min.nil? && !@max.nil? && @min > @max
|
17
|
+
|
18
|
+
return if @is.nil?
|
19
|
+
raise ArgumentError, 'is must be an integer greater than zero' if !@is.is_a?(Integer) || !@is.positive?
|
20
|
+
raise ArgumentError, 'is cannot be combined with min or max' if !@min.nil? || !@max.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate_param!(attr_name, params)
|
24
|
+
param = params[attr_name]
|
25
|
+
|
26
|
+
return unless param.respond_to?(:length)
|
27
|
+
|
28
|
+
return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max) || (!@is.nil? && param.length != @is)
|
29
|
+
|
30
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: build_message)
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_message
|
34
|
+
if options_key?(:message)
|
35
|
+
@option[:message]
|
36
|
+
elsif @min && @max
|
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
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
module Validators
|
6
6
|
class PresenceValidator < Base
|
7
7
|
def validate_param!(attr_name, params)
|
8
|
-
return if params.
|
8
|
+
return if params.try(:key?, attr_name)
|
9
9
|
|
10
10
|
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
|
11
11
|
end
|
@@ -5,8 +5,8 @@ module Grape
|
|
5
5
|
module Validators
|
6
6
|
class RegexpValidator < Base
|
7
7
|
def validate_param!(attr_name, params)
|
8
|
-
return unless params.
|
9
|
-
return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.match?((options_key?(:value) ? @option[:value] : @option)) }
|
8
|
+
return unless params.try(:key?, attr_name)
|
9
|
+
return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.scrub.match?((options_key?(:value) ? @option[:value] : @option)) }
|
10
10
|
|
11
11
|
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
|
12
12
|
end
|