grape 0.1.1 → 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 +7 -0
- data/CHANGELOG.md +1167 -0
- data/CONTRIBUTING.md +156 -0
- data/LICENSE +1 -1
- data/README.md +4192 -0
- data/UPGRADING.md +1697 -0
- data/grape.gemspec +28 -105
- data/grape.png +0 -0
- data/lib/grape/api/helpers.rb +9 -0
- data/lib/grape/api/instance.rb +247 -0
- data/lib/grape/api.rb +158 -194
- data/lib/grape/content_types.rb +31 -0
- data/lib/grape/cookies.rb +50 -0
- data/lib/grape/dry_types.rb +10 -0
- data/lib/grape/dsl/api.rb +17 -0
- data/lib/grape/dsl/callbacks.rb +69 -0
- data/lib/grape/dsl/configuration.rb +15 -0
- data/lib/grape/dsl/desc.rb +124 -0
- data/lib/grape/dsl/headers.rb +21 -0
- data/lib/grape/dsl/helpers.rb +108 -0
- data/lib/grape/dsl/inside_route.rb +454 -0
- data/lib/grape/dsl/logger.rb +22 -0
- data/lib/grape/dsl/middleware.rb +54 -0
- data/lib/grape/dsl/parameters.rb +266 -0
- data/lib/grape/dsl/request_response.rb +171 -0
- data/lib/grape/dsl/routing.rb +248 -0
- data/lib/grape/dsl/settings.rb +179 -0
- data/lib/grape/dsl/validations.rb +57 -0
- data/lib/grape/endpoint.rb +398 -68
- data/lib/grape/env.rb +20 -0
- data/lib/grape/error_formatter/base.rb +68 -0
- data/lib/grape/error_formatter/json.rb +28 -0
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +21 -0
- data/lib/grape/error_formatter/xml.rb +11 -0
- data/lib/grape/error_formatter.rb +15 -0
- data/lib/grape/exceptions/base.rb +76 -0
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +11 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +11 -0
- data/lib/grape/exceptions/invalid_formatter.rb +11 -0
- data/lib/grape/exceptions/invalid_message_body.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/invalid_response.rb +11 -0
- data/lib/grape/exceptions/invalid_version_header.rb +11 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +11 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +11 -0
- data/lib/grape/exceptions/method_not_allowed.rb +11 -0
- data/lib/grape/exceptions/missing_group_type.rb +13 -0
- data/lib/grape/exceptions/missing_mime_type.rb +11 -0
- data/lib/grape/exceptions/missing_option.rb +11 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_options.rb +11 -0
- data/lib/grape/exceptions/unknown_parameter.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/unknown_validator.rb +11 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +13 -0
- data/lib/grape/exceptions/validation.rb +25 -0
- data/lib/grape/exceptions/validation_array_errors.rb +14 -0
- data/lib/grape/exceptions/validation_errors.rb +53 -0
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +24 -0
- data/lib/grape/extensions/hash.rb +27 -0
- data/lib/grape/extensions/hashie/mash.rb +24 -0
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +13 -0
- data/lib/grape/formatter/serializable_hash.rb +39 -0
- data/lib/grape/formatter/txt.rb +11 -0
- data/lib/grape/formatter/xml.rb +13 -0
- data/lib/grape/formatter.rb +17 -0
- data/lib/grape/json.rb +10 -0
- data/lib/grape/locale/en.yml +59 -0
- data/lib/grape/middleware/auth/base.rb +23 -0
- data/lib/grape/middleware/auth/dsl.rb +39 -0
- data/lib/grape/middleware/auth/strategies.rb +25 -0
- data/lib/grape/middleware/auth/strategy_info.rb +15 -0
- data/lib/grape/middleware/base.rb +89 -18
- data/lib/grape/middleware/error.rb +129 -10
- data/lib/grape/middleware/filter.rb +19 -0
- data/lib/grape/middleware/formatter.rb +124 -82
- data/lib/grape/middleware/globals.rb +14 -0
- data/lib/grape/middleware/stack.rb +109 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +38 -0
- data/lib/grape/middleware/versioner/base.rb +74 -0
- data/lib/grape/middleware/versioner/header.rb +127 -0
- data/lib/grape/middleware/versioner/param.rb +32 -0
- data/lib/grape/middleware/versioner/path.rb +40 -0
- data/lib/grape/middleware/versioner.rb +21 -21
- data/lib/grape/namespace.rb +46 -0
- 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 +14 -0
- data/lib/grape/parser/xml.rb +14 -0
- data/lib/grape/parser.rb +15 -0
- data/lib/grape/path.rb +76 -0
- data/lib/grape/presenters/presenter.rb +11 -0
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +190 -0
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +81 -0
- data/lib/grape/router/route.rb +62 -0
- data/lib/grape/router.rb +190 -0
- data/lib/grape/serve_stream/file_body.rb +36 -0
- data/lib/grape/serve_stream/sendfile_response.rb +21 -0
- data/lib/grape/serve_stream/stream_response.rb +23 -0
- data/lib/grape/types/invalid_value.rb +8 -0
- data/lib/grape/util/base_inheritable.rb +43 -0
- data/lib/grape/util/cache.rb +17 -0
- data/lib/grape/util/endpoint_configuration.rb +8 -0
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_setting.rb +100 -0
- data/lib/grape/util/inheritable_values.rb +29 -0
- 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 +15 -0
- data/lib/grape/util/stackable_values.rb +36 -0
- data/lib/grape/util/strict_hash_configuration.rb +108 -0
- data/lib/grape/validations/attributes_doc.rb +60 -0
- data/lib/grape/validations/attributes_iterator.rb +62 -0
- data/lib/grape/validations/contract_scope.rb +34 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
- data/lib/grape/validations/params_scope.rb +538 -0
- data/lib/grape/validations/single_attribute_iterator.rb +26 -0
- data/lib/grape/validations/types/array_coercer.rb +61 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +164 -0
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
- data/lib/grape/validations/types/dry_type_coercer.rb +66 -0
- data/lib/grape/validations/types/file.rb +31 -0
- data/lib/grape/validations/types/invalid_value.rb +17 -0
- data/lib/grape/validations/types/json.rb +71 -0
- data/lib/grape/validations/types/multiple_type_coercer.rb +57 -0
- data/lib/grape/validations/types/primitive_coercer.rb +73 -0
- data/lib/grape/validations/types/set_coercer.rb +35 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +51 -0
- data/lib/grape/validations/types.rb +213 -0
- data/lib/grape/validations/validator_factory.rb +15 -0
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
- data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
- data/lib/grape/validations/validators/as_validator.rb +14 -0
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
- data/lib/grape/validations/validators/base.rb +88 -0
- data/lib/grape/validations/validators/coerce_validator.rb +75 -0
- data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
- data/lib/grape/validations/validators/default_validator.rb +37 -0
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
- data/lib/grape/validations/validators/except_values_validator.rb +24 -0
- data/lib/grape/validations/validators/length_validator.rb +49 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +34 -0
- data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
- data/lib/grape/validations/validators/presence_validator.rb +15 -0
- data/lib/grape/validations/validators/regexp_validator.rb +16 -0
- data/lib/grape/validations/validators/same_as_validator.rb +29 -0
- data/lib/grape/validations/validators/values_validator.rb +60 -0
- data/lib/grape/validations.rb +21 -0
- data/lib/grape/version.rb +6 -0
- data/lib/grape/xml.rb +10 -0
- data/lib/grape.rb +81 -17
- metadata +250 -192
- data/.document +0 -5
- data/.gitignore +0 -23
- data/.rspec +0 -2
- data/.rvmrc +0 -1
- data/Gemfile +0 -21
- data/Gemfile.lock +0 -58
- data/README.markdown +0 -75
- data/Rakefile +0 -70
- data/VERSION +0 -1
- data/autotest/discover.rb +0 -1
- data/lib/grape/middleware/auth/basic.rb +0 -30
- data/lib/grape/middleware/auth/oauth2.rb +0 -55
- data/lib/grape/middleware/prefixer.rb +0 -21
- data/lib/grape/middleware_stack.rb +0 -35
- data/spec/grape/api_spec.rb +0 -343
- data/spec/grape/endpoint_spec.rb +0 -104
- data/spec/grape/middleware/auth/basic_spec.rb +0 -31
- data/spec/grape/middleware/auth/oauth2_spec.rb +0 -88
- data/spec/grape/middleware/base_spec.rb +0 -63
- data/spec/grape/middleware/error_spec.rb +0 -49
- data/spec/grape/middleware/formatter_spec.rb +0 -87
- data/spec/grape/middleware/prefixer_spec.rb +0 -30
- data/spec/grape/middleware/versioner_spec.rb +0 -40
- data/spec/grape/middleware_stack_spec.rb +0 -47
- data/spec/grape_spec.rb +0 -1
- data/spec/spec_helper.rb +0 -20
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
# Module for code related to grape's system for
|
|
6
|
+
# coercion and type validation of incoming request
|
|
7
|
+
# parameters.
|
|
8
|
+
#
|
|
9
|
+
# Grape uses a number of tests and assertions to
|
|
10
|
+
# work out exactly how a parameter should be handled,
|
|
11
|
+
# based on the +type+ and +coerce_with+ options that
|
|
12
|
+
# may be supplied to {Grape::Dsl::Parameters#requires}
|
|
13
|
+
# and {Grape::Dsl::Parameters#optional}. The main
|
|
14
|
+
# entry point for this process is {Types.build_coercer}.
|
|
15
|
+
module Types
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
PRIMITIVES = [
|
|
19
|
+
# Numerical
|
|
20
|
+
Integer,
|
|
21
|
+
Float,
|
|
22
|
+
BigDecimal,
|
|
23
|
+
Numeric,
|
|
24
|
+
|
|
25
|
+
# Date/time
|
|
26
|
+
Date,
|
|
27
|
+
DateTime,
|
|
28
|
+
Time,
|
|
29
|
+
|
|
30
|
+
# Misc
|
|
31
|
+
Grape::API::Boolean,
|
|
32
|
+
String,
|
|
33
|
+
Symbol,
|
|
34
|
+
TrueClass,
|
|
35
|
+
FalseClass
|
|
36
|
+
].freeze
|
|
37
|
+
|
|
38
|
+
# Types representing data structures.
|
|
39
|
+
STRUCTURES = [Hash, Array, Set].freeze
|
|
40
|
+
|
|
41
|
+
SPECIAL = {
|
|
42
|
+
::JSON => Json,
|
|
43
|
+
Array[JSON] => JsonArray,
|
|
44
|
+
::File => File,
|
|
45
|
+
Rack::Multipart::UploadedFile => File
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
GROUPS = [Array, Hash, JSON, Array[JSON]].freeze
|
|
49
|
+
|
|
50
|
+
# Is the given class a primitive type as recognized by Grape?
|
|
51
|
+
#
|
|
52
|
+
# @param type [Class] type to check
|
|
53
|
+
# @return [Boolean] whether or not the type is known by Grape as a valid
|
|
54
|
+
# type for a single value
|
|
55
|
+
def primitive?(type)
|
|
56
|
+
PRIMITIVES.include?(type)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Is the given class a standard data structure (collection or map)
|
|
60
|
+
# as recognized by Grape?
|
|
61
|
+
#
|
|
62
|
+
# @param type [Class] type to check
|
|
63
|
+
# @return [Boolean] whether or not the type is known by Grape as a valid
|
|
64
|
+
# data structure type
|
|
65
|
+
def structure?(type)
|
|
66
|
+
STRUCTURES.include?(type)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Is the declared type in fact an array of multiple allowed types?
|
|
70
|
+
# For example the declaration +types: [Integer,String]+ will attempt
|
|
71
|
+
# first to coerce given values to integer, but will also accept any
|
|
72
|
+
# other string.
|
|
73
|
+
#
|
|
74
|
+
# @param type [Array<Class>,Set<Class>] type (or type list!) to check
|
|
75
|
+
# @return [Boolean] +true+ if the given value will be treated as
|
|
76
|
+
# a list of types.
|
|
77
|
+
def multiple?(type)
|
|
78
|
+
(type.is_a?(Array) || type.is_a?(Set)) && type.size > 1
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Does Grape provide special coercion and validation
|
|
82
|
+
# routines for the given class? This does not include
|
|
83
|
+
# automatic handling for primitives, structures and
|
|
84
|
+
# otherwise recognized types. See {Types::SPECIAL}.
|
|
85
|
+
#
|
|
86
|
+
# @param type [Class] type to check
|
|
87
|
+
# @return [Boolean] +true+ if special routines are available
|
|
88
|
+
def special?(type)
|
|
89
|
+
SPECIAL.key? type
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Is the declared type a supported group type?
|
|
93
|
+
# Currently supported group types are Array, Hash, JSON, and Array[JSON]
|
|
94
|
+
#
|
|
95
|
+
# @param type [Array<Class>,Class] type to check
|
|
96
|
+
# @return [Boolean] +true+ if the type is a supported group type
|
|
97
|
+
def group?(type)
|
|
98
|
+
GROUPS.include? type
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# A valid custom type must implement a class-level `parse` method, taking
|
|
102
|
+
# one String argument and returning the parsed value in its correct type.
|
|
103
|
+
#
|
|
104
|
+
# @param type [Class] type to check
|
|
105
|
+
# @return [Boolean] whether or not the type can be used as a custom type
|
|
106
|
+
def custom?(type)
|
|
107
|
+
!primitive?(type) &&
|
|
108
|
+
!structure?(type) &&
|
|
109
|
+
!multiple?(type) &&
|
|
110
|
+
type.respond_to?(:parse) &&
|
|
111
|
+
type.method(:parse).arity == 1
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Is the declared type an +Array+ or +Set+ of a {#custom?} type?
|
|
115
|
+
#
|
|
116
|
+
# @param type [Array<Class>,Class] type to check
|
|
117
|
+
# @return [Boolean] true if +type+ is a collection of a type that implements
|
|
118
|
+
# its own +#parse+ method.
|
|
119
|
+
def collection_of_custom?(type)
|
|
120
|
+
(type.is_a?(Array) || type.is_a?(Set)) &&
|
|
121
|
+
type.length == 1 &&
|
|
122
|
+
(custom?(type.first) || special?(type.first))
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def map_special(type)
|
|
126
|
+
SPECIAL.fetch(type, type)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Chooses the best coercer for the given type. For example, if the type
|
|
130
|
+
# is Integer, it will return a coercer which will be able to coerce a value
|
|
131
|
+
# to the integer.
|
|
132
|
+
#
|
|
133
|
+
# There are a few very special coercers which might be returned.
|
|
134
|
+
#
|
|
135
|
+
# +Grape::Types::MultipleTypeCoercer+ is a coercer which is returned when
|
|
136
|
+
# the given type implies values in an array with different types.
|
|
137
|
+
# For example, +[Integer, String]+ allows integer and string values in
|
|
138
|
+
# an array.
|
|
139
|
+
#
|
|
140
|
+
# +Grape::Types::CustomTypeCoercer+ is a coercer which is returned when
|
|
141
|
+
# a method is specified by a user with +coerce_with+ option or the user
|
|
142
|
+
# specifies a custom type which implements requirments of
|
|
143
|
+
# +Grape::Types::CustomTypeCoercer+.
|
|
144
|
+
#
|
|
145
|
+
# +Grape::Types::CustomTypeCollectionCoercer+ is a very similar to the
|
|
146
|
+
# previous one, but it expects an array or set of values having a custom
|
|
147
|
+
# type implemented by the user.
|
|
148
|
+
#
|
|
149
|
+
# There is also a group of custom types implemented by Grape, check
|
|
150
|
+
# +Grape::Validations::Types::SPECIAL+ to get the full list.
|
|
151
|
+
#
|
|
152
|
+
# @param type [Class] the type to which input strings
|
|
153
|
+
# should be coerced
|
|
154
|
+
# @param method [Class,#call] the coercion method to use
|
|
155
|
+
# @return [Object] object to be used
|
|
156
|
+
# for coercion and type validation
|
|
157
|
+
def build_coercer(type, method: nil, strict: false)
|
|
158
|
+
cache_instance(type, method, strict) do
|
|
159
|
+
create_coercer_instance(type, method, strict)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def create_coercer_instance(type, method, strict)
|
|
164
|
+
# Maps a custom type provided by Grape, it doesn't map types wrapped by collections!!!
|
|
165
|
+
type = Types.map_special(type)
|
|
166
|
+
|
|
167
|
+
# Use a special coercer for multiply-typed parameters.
|
|
168
|
+
if Types.multiple?(type)
|
|
169
|
+
MultipleTypeCoercer.new(type, method)
|
|
170
|
+
|
|
171
|
+
# Use a special coercer for custom types and coercion methods.
|
|
172
|
+
elsif method || Types.custom?(type)
|
|
173
|
+
CustomTypeCoercer.new(type, method)
|
|
174
|
+
|
|
175
|
+
# Special coercer for collections of types that implement a parse method.
|
|
176
|
+
# CustomTypeCoercer (above) already handles such types when an explicit coercion
|
|
177
|
+
# method is supplied.
|
|
178
|
+
elsif Types.collection_of_custom?(type)
|
|
179
|
+
Types::CustomTypeCollectionCoercer.new(
|
|
180
|
+
Types.map_special(type.first), type.is_a?(Set)
|
|
181
|
+
)
|
|
182
|
+
else
|
|
183
|
+
DryTypeCoercer.coercer_instance_for(type, strict)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def cache_instance(type, method, strict, &_block)
|
|
188
|
+
key = cache_key(type, method, strict)
|
|
189
|
+
|
|
190
|
+
return @__cache[key] if @__cache.key?(key)
|
|
191
|
+
|
|
192
|
+
instance = yield
|
|
193
|
+
|
|
194
|
+
@__cache_write_lock.synchronize do
|
|
195
|
+
@__cache[key] = instance
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
instance
|
|
199
|
+
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
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
class ValidatorFactory
|
|
6
|
+
def self.create_validator(options)
|
|
7
|
+
options[:validator_class].new(options[:attributes],
|
|
8
|
+
options[:options],
|
|
9
|
+
options[:required],
|
|
10
|
+
options[:params_scope],
|
|
11
|
+
options[:opts])
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class AllOrNoneOfValidator < MultipleParamsBase
|
|
7
|
+
def validate_params!(params)
|
|
8
|
+
keys = keys_in_common(params)
|
|
9
|
+
return if keys.empty? || keys.length == all_keys.length
|
|
10
|
+
|
|
11
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:all_or_none))
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class AllowBlankValidator < Base
|
|
7
|
+
def validate_param!(attr_name, params)
|
|
8
|
+
return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)
|
|
9
|
+
|
|
10
|
+
value = params[attr_name]
|
|
11
|
+
value = value.scrub if value.respond_to?(:scrub)
|
|
12
|
+
|
|
13
|
+
return if value == false || value.present?
|
|
14
|
+
|
|
15
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:blank))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class AsValidator < Base
|
|
7
|
+
# We use a validator for renaming parameters. This is just a marker for
|
|
8
|
+
# the parameter scope to handle the renaming. No actual validation
|
|
9
|
+
# happens here.
|
|
10
|
+
def validate_param!(*); end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class AtLeastOneOfValidator < MultipleParamsBase
|
|
7
|
+
def validate_params!(params)
|
|
8
|
+
return unless keys_in_common(params).empty?
|
|
9
|
+
|
|
10
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:at_least_one))
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class Base
|
|
7
|
+
attr_reader :attrs
|
|
8
|
+
|
|
9
|
+
# Creates a new Validator from options specified
|
|
10
|
+
# by a +requires+ or +optional+ directive during
|
|
11
|
+
# parameter definition.
|
|
12
|
+
# @param attrs [Array] names of attributes to which the Validator applies
|
|
13
|
+
# @param options [Object] implementation-dependent Validator options
|
|
14
|
+
# @param required [Boolean] attribute(s) are required or optional
|
|
15
|
+
# @param scope [ParamsScope] parent scope for this Validator
|
|
16
|
+
# @param opts [Hash] additional validation options
|
|
17
|
+
def initialize(attrs, options, required, scope, opts)
|
|
18
|
+
@attrs = Array(attrs)
|
|
19
|
+
@option = options
|
|
20
|
+
@required = required
|
|
21
|
+
@scope = scope
|
|
22
|
+
@fail_fast = opts[:fail_fast]
|
|
23
|
+
@allow_blank = opts[:allow_blank]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Validates a given request.
|
|
27
|
+
# @note Override #validate! unless you need to access the entire request.
|
|
28
|
+
# @param request [Grape::Request] the request currently being handled
|
|
29
|
+
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
30
|
+
# @return [void]
|
|
31
|
+
def validate(request)
|
|
32
|
+
return unless @scope.should_validate?(request.params)
|
|
33
|
+
|
|
34
|
+
validate!(request.params)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Validates a given parameter hash.
|
|
38
|
+
# @note Override #validate if you need to access the entire request.
|
|
39
|
+
# @param params [Hash] parameters to validate
|
|
40
|
+
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
41
|
+
# @return [void]
|
|
42
|
+
def validate!(params)
|
|
43
|
+
attributes = SingleAttributeIterator.new(self, @scope, params)
|
|
44
|
+
# we collect errors inside array because
|
|
45
|
+
# there may be more than one error per field
|
|
46
|
+
array_errors = []
|
|
47
|
+
|
|
48
|
+
attributes.each do |val, attr_name, empty_val|
|
|
49
|
+
next if !@scope.required? && empty_val
|
|
50
|
+
next unless @scope.meets_dependency?(val, params)
|
|
51
|
+
|
|
52
|
+
validate_param!(attr_name, val) if @required || val.try(:key?, attr_name)
|
|
53
|
+
rescue Grape::Exceptions::Validation => e
|
|
54
|
+
array_errors << e
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.inherited(klass)
|
|
61
|
+
super
|
|
62
|
+
Validations.register(klass)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def message(default_key = nil)
|
|
66
|
+
options = instance_variable_get(:@option)
|
|
67
|
+
options_key?(:message) ? options[:message] : default_key
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def options_key?(key, options = nil)
|
|
71
|
+
options = instance_variable_get(:@option) if options.nil?
|
|
72
|
+
options.try(:key?, key) && !options[key].nil?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def fail_fast?
|
|
76
|
+
@fail_fast
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
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
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class CoerceValidator < Base
|
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
|
8
|
+
super
|
|
9
|
+
|
|
10
|
+
@converter = if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
|
|
11
|
+
type
|
|
12
|
+
else
|
|
13
|
+
Types.build_coercer(type, method: @option[:method])
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def validate_param!(attr_name, params)
|
|
18
|
+
raise validation_exception(attr_name) unless params.is_a? Hash
|
|
19
|
+
|
|
20
|
+
new_value = coerce_value(params[attr_name])
|
|
21
|
+
|
|
22
|
+
raise validation_exception(attr_name, new_value.message) unless valid_type?(new_value)
|
|
23
|
+
|
|
24
|
+
# Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
|
|
25
|
+
# which looses wrappers for hashes and arrays after reassigning values
|
|
26
|
+
#
|
|
27
|
+
# h = Hashie::Mash.new(list: [1, 2, 3, 4])
|
|
28
|
+
# => #<Hashie::Mash list=#<Hashie::Array [1, 2, 3, 4]>>
|
|
29
|
+
# list = h.list
|
|
30
|
+
# h[:list] = list
|
|
31
|
+
# h
|
|
32
|
+
# => #<Hashie::Mash list=[1, 2, 3, 4]>
|
|
33
|
+
return if params[attr_name].instance_of?(new_value.class) && params[attr_name] == new_value
|
|
34
|
+
|
|
35
|
+
params[attr_name] = new_value
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# @!attribute [r] converter
|
|
41
|
+
# Object that will be used for parameter coercion and type checking.
|
|
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
|
+
|
|
52
|
+
def coerce_value(val)
|
|
53
|
+
converter.call(val)
|
|
54
|
+
# Some custom types might fail, so it should be treated as an invalid value
|
|
55
|
+
rescue StandardError
|
|
56
|
+
Types::InvalidValue.new
|
|
57
|
+
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
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -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
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class DefaultValidator < Base
|
|
7
|
+
def initialize(attrs, options, required, scope, opts = {})
|
|
8
|
+
@default = options
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def validate_param!(attr_name, params)
|
|
13
|
+
params[attr_name] = if @default.is_a? Proc
|
|
14
|
+
if @default.parameters.empty?
|
|
15
|
+
@default.call
|
|
16
|
+
else
|
|
17
|
+
@default.call(params)
|
|
18
|
+
end
|
|
19
|
+
elsif @default.frozen? || !@default.duplicable?
|
|
20
|
+
@default
|
|
21
|
+
else
|
|
22
|
+
@default.dup
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def validate!(params)
|
|
27
|
+
attrs = SingleAttributeIterator.new(self, @scope, params)
|
|
28
|
+
attrs.each do |resource_params, attr_name|
|
|
29
|
+
next unless @scope.meets_dependency?(resource_params, params)
|
|
30
|
+
|
|
31
|
+
validate_param!(attr_name, resource_params) if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class ExactlyOneOfValidator < MultipleParamsBase
|
|
7
|
+
def validate_params!(params)
|
|
8
|
+
keys = keys_in_common(params)
|
|
9
|
+
return if keys.length == 1
|
|
10
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.empty?
|
|
11
|
+
|
|
12
|
+
raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class ExceptValuesValidator < Base
|
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
|
8
|
+
@except = options.is_a?(Hash) ? options[:value] : options
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def validate_param!(attr_name, params)
|
|
13
|
+
return unless params.try(:key?, attr_name)
|
|
14
|
+
|
|
15
|
+
excepts = @except.is_a?(Proc) ? @except.call : @except
|
|
16
|
+
return if excepts.nil?
|
|
17
|
+
|
|
18
|
+
param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
|
|
19
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:except_values)) if param_array.any? { |param| excepts.include?(param) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -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
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class MultipleParamsBase < Base
|
|
7
|
+
def validate!(params)
|
|
8
|
+
attributes = MultipleAttributesIterator.new(self, @scope, params)
|
|
9
|
+
array_errors = []
|
|
10
|
+
|
|
11
|
+
attributes.each do |resource_params|
|
|
12
|
+
validate_params!(resource_params)
|
|
13
|
+
rescue Grape::Exceptions::Validation => e
|
|
14
|
+
array_errors << e
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def keys_in_common(resource_params)
|
|
23
|
+
return [] unless resource_params.is_a?(Hash)
|
|
24
|
+
|
|
25
|
+
all_keys & resource_params.keys.map! { |attr| @scope.full_name(attr) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def all_keys
|
|
29
|
+
attrs.map { |attr| @scope.full_name(attr) }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class MutualExclusionValidator < MultipleParamsBase
|
|
7
|
+
def validate_params!(params)
|
|
8
|
+
keys = keys_in_common(params)
|
|
9
|
+
return if keys.length <= 1
|
|
10
|
+
|
|
11
|
+
raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|