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.
Files changed (197) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1167 -0
  3. data/CONTRIBUTING.md +156 -0
  4. data/LICENSE +1 -1
  5. data/README.md +4192 -0
  6. data/UPGRADING.md +1697 -0
  7. data/grape.gemspec +28 -105
  8. data/grape.png +0 -0
  9. data/lib/grape/api/helpers.rb +9 -0
  10. data/lib/grape/api/instance.rb +247 -0
  11. data/lib/grape/api.rb +158 -194
  12. data/lib/grape/content_types.rb +31 -0
  13. data/lib/grape/cookies.rb +50 -0
  14. data/lib/grape/dry_types.rb +10 -0
  15. data/lib/grape/dsl/api.rb +17 -0
  16. data/lib/grape/dsl/callbacks.rb +69 -0
  17. data/lib/grape/dsl/configuration.rb +15 -0
  18. data/lib/grape/dsl/desc.rb +124 -0
  19. data/lib/grape/dsl/headers.rb +21 -0
  20. data/lib/grape/dsl/helpers.rb +108 -0
  21. data/lib/grape/dsl/inside_route.rb +454 -0
  22. data/lib/grape/dsl/logger.rb +22 -0
  23. data/lib/grape/dsl/middleware.rb +54 -0
  24. data/lib/grape/dsl/parameters.rb +266 -0
  25. data/lib/grape/dsl/request_response.rb +171 -0
  26. data/lib/grape/dsl/routing.rb +248 -0
  27. data/lib/grape/dsl/settings.rb +179 -0
  28. data/lib/grape/dsl/validations.rb +57 -0
  29. data/lib/grape/endpoint.rb +398 -68
  30. data/lib/grape/env.rb +20 -0
  31. data/lib/grape/error_formatter/base.rb +68 -0
  32. data/lib/grape/error_formatter/json.rb +28 -0
  33. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  34. data/lib/grape/error_formatter/txt.rb +21 -0
  35. data/lib/grape/error_formatter/xml.rb +11 -0
  36. data/lib/grape/error_formatter.rb +15 -0
  37. data/lib/grape/exceptions/base.rb +76 -0
  38. data/lib/grape/exceptions/conflicting_types.rb +11 -0
  39. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  40. data/lib/grape/exceptions/incompatible_option_values.rb +11 -0
  41. data/lib/grape/exceptions/invalid_accept_header.rb +11 -0
  42. data/lib/grape/exceptions/invalid_formatter.rb +11 -0
  43. data/lib/grape/exceptions/invalid_message_body.rb +11 -0
  44. data/lib/grape/exceptions/invalid_parameters.rb +11 -0
  45. data/lib/grape/exceptions/invalid_response.rb +11 -0
  46. data/lib/grape/exceptions/invalid_version_header.rb +11 -0
  47. data/lib/grape/exceptions/invalid_versioner_option.rb +11 -0
  48. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +11 -0
  49. data/lib/grape/exceptions/method_not_allowed.rb +11 -0
  50. data/lib/grape/exceptions/missing_group_type.rb +13 -0
  51. data/lib/grape/exceptions/missing_mime_type.rb +11 -0
  52. data/lib/grape/exceptions/missing_option.rb +11 -0
  53. data/lib/grape/exceptions/missing_vendor_option.rb +11 -0
  54. data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
  55. data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
  56. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  57. data/lib/grape/exceptions/unknown_options.rb +11 -0
  58. data/lib/grape/exceptions/unknown_parameter.rb +11 -0
  59. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  60. data/lib/grape/exceptions/unknown_validator.rb +11 -0
  61. data/lib/grape/exceptions/unsupported_group_type.rb +13 -0
  62. data/lib/grape/exceptions/validation.rb +25 -0
  63. data/lib/grape/exceptions/validation_array_errors.rb +14 -0
  64. data/lib/grape/exceptions/validation_errors.rb +53 -0
  65. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +24 -0
  66. data/lib/grape/extensions/hash.rb +27 -0
  67. data/lib/grape/extensions/hashie/mash.rb +24 -0
  68. data/lib/grape/formatter/base.rb +16 -0
  69. data/lib/grape/formatter/json.rb +13 -0
  70. data/lib/grape/formatter/serializable_hash.rb +39 -0
  71. data/lib/grape/formatter/txt.rb +11 -0
  72. data/lib/grape/formatter/xml.rb +13 -0
  73. data/lib/grape/formatter.rb +17 -0
  74. data/lib/grape/json.rb +10 -0
  75. data/lib/grape/locale/en.yml +59 -0
  76. data/lib/grape/middleware/auth/base.rb +23 -0
  77. data/lib/grape/middleware/auth/dsl.rb +39 -0
  78. data/lib/grape/middleware/auth/strategies.rb +25 -0
  79. data/lib/grape/middleware/auth/strategy_info.rb +15 -0
  80. data/lib/grape/middleware/base.rb +89 -18
  81. data/lib/grape/middleware/error.rb +129 -10
  82. data/lib/grape/middleware/filter.rb +19 -0
  83. data/lib/grape/middleware/formatter.rb +124 -82
  84. data/lib/grape/middleware/globals.rb +14 -0
  85. data/lib/grape/middleware/stack.rb +109 -0
  86. data/lib/grape/middleware/versioner/accept_version_header.rb +38 -0
  87. data/lib/grape/middleware/versioner/base.rb +74 -0
  88. data/lib/grape/middleware/versioner/header.rb +127 -0
  89. data/lib/grape/middleware/versioner/param.rb +32 -0
  90. data/lib/grape/middleware/versioner/path.rb +40 -0
  91. data/lib/grape/middleware/versioner.rb +21 -21
  92. data/lib/grape/namespace.rb +46 -0
  93. data/lib/grape/params_builder/base.rb +18 -0
  94. data/lib/grape/params_builder/hash.rb +11 -0
  95. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  96. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  97. data/lib/grape/params_builder.rb +32 -0
  98. data/lib/grape/parser/base.rb +16 -0
  99. data/lib/grape/parser/json.rb +14 -0
  100. data/lib/grape/parser/xml.rb +14 -0
  101. data/lib/grape/parser.rb +15 -0
  102. data/lib/grape/path.rb +76 -0
  103. data/lib/grape/presenters/presenter.rb +11 -0
  104. data/lib/grape/railtie.rb +9 -0
  105. data/lib/grape/request.rb +190 -0
  106. data/lib/grape/router/base_route.rb +39 -0
  107. data/lib/grape/router/greedy_route.rb +20 -0
  108. data/lib/grape/router/pattern.rb +81 -0
  109. data/lib/grape/router/route.rb +62 -0
  110. data/lib/grape/router.rb +190 -0
  111. data/lib/grape/serve_stream/file_body.rb +36 -0
  112. data/lib/grape/serve_stream/sendfile_response.rb +21 -0
  113. data/lib/grape/serve_stream/stream_response.rb +23 -0
  114. data/lib/grape/types/invalid_value.rb +8 -0
  115. data/lib/grape/util/base_inheritable.rb +43 -0
  116. data/lib/grape/util/cache.rb +17 -0
  117. data/lib/grape/util/endpoint_configuration.rb +8 -0
  118. data/lib/grape/util/header.rb +13 -0
  119. data/lib/grape/util/inheritable_setting.rb +100 -0
  120. data/lib/grape/util/inheritable_values.rb +29 -0
  121. data/lib/grape/util/lazy/block.rb +29 -0
  122. data/lib/grape/util/lazy/value.rb +38 -0
  123. data/lib/grape/util/lazy/value_array.rb +21 -0
  124. data/lib/grape/util/lazy/value_enumerable.rb +34 -0
  125. data/lib/grape/util/lazy/value_hash.rb +21 -0
  126. data/lib/grape/util/media_type.rb +70 -0
  127. data/lib/grape/util/registry.rb +27 -0
  128. data/lib/grape/util/reverse_stackable_values.rb +15 -0
  129. data/lib/grape/util/stackable_values.rb +36 -0
  130. data/lib/grape/util/strict_hash_configuration.rb +108 -0
  131. data/lib/grape/validations/attributes_doc.rb +60 -0
  132. data/lib/grape/validations/attributes_iterator.rb +62 -0
  133. data/lib/grape/validations/contract_scope.rb +34 -0
  134. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  135. data/lib/grape/validations/params_scope.rb +538 -0
  136. data/lib/grape/validations/single_attribute_iterator.rb +26 -0
  137. data/lib/grape/validations/types/array_coercer.rb +61 -0
  138. data/lib/grape/validations/types/custom_type_coercer.rb +164 -0
  139. data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
  140. data/lib/grape/validations/types/dry_type_coercer.rb +66 -0
  141. data/lib/grape/validations/types/file.rb +31 -0
  142. data/lib/grape/validations/types/invalid_value.rb +17 -0
  143. data/lib/grape/validations/types/json.rb +71 -0
  144. data/lib/grape/validations/types/multiple_type_coercer.rb +57 -0
  145. data/lib/grape/validations/types/primitive_coercer.rb +73 -0
  146. data/lib/grape/validations/types/set_coercer.rb +35 -0
  147. data/lib/grape/validations/types/variant_collection_coercer.rb +51 -0
  148. data/lib/grape/validations/types.rb +213 -0
  149. data/lib/grape/validations/validator_factory.rb +15 -0
  150. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  151. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  152. data/lib/grape/validations/validators/as_validator.rb +14 -0
  153. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  154. data/lib/grape/validations/validators/base.rb +88 -0
  155. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  156. data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
  157. data/lib/grape/validations/validators/default_validator.rb +37 -0
  158. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  159. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  160. data/lib/grape/validations/validators/length_validator.rb +49 -0
  161. data/lib/grape/validations/validators/multiple_params_base.rb +34 -0
  162. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  163. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  164. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  165. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  166. data/lib/grape/validations/validators/values_validator.rb +60 -0
  167. data/lib/grape/validations.rb +21 -0
  168. data/lib/grape/version.rb +6 -0
  169. data/lib/grape/xml.rb +10 -0
  170. data/lib/grape.rb +81 -17
  171. metadata +250 -192
  172. data/.document +0 -5
  173. data/.gitignore +0 -23
  174. data/.rspec +0 -2
  175. data/.rvmrc +0 -1
  176. data/Gemfile +0 -21
  177. data/Gemfile.lock +0 -58
  178. data/README.markdown +0 -75
  179. data/Rakefile +0 -70
  180. data/VERSION +0 -1
  181. data/autotest/discover.rb +0 -1
  182. data/lib/grape/middleware/auth/basic.rb +0 -30
  183. data/lib/grape/middleware/auth/oauth2.rb +0 -55
  184. data/lib/grape/middleware/prefixer.rb +0 -21
  185. data/lib/grape/middleware_stack.rb +0 -35
  186. data/spec/grape/api_spec.rb +0 -343
  187. data/spec/grape/endpoint_spec.rb +0 -104
  188. data/spec/grape/middleware/auth/basic_spec.rb +0 -31
  189. data/spec/grape/middleware/auth/oauth2_spec.rb +0 -88
  190. data/spec/grape/middleware/base_spec.rb +0 -63
  191. data/spec/grape/middleware/error_spec.rb +0 -49
  192. data/spec/grape/middleware/formatter_spec.rb +0 -87
  193. data/spec/grape/middleware/prefixer_spec.rb +0 -30
  194. data/spec/grape/middleware/versioner_spec.rb +0 -40
  195. data/spec/grape/middleware_stack_spec.rb +0 -47
  196. data/spec/grape_spec.rb +0 -1
  197. 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