grape 1.5.2 → 1.6.2
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 +47 -0
- data/CONTRIBUTING.md +2 -1
- data/README.md +33 -3
- data/UPGRADING.md +71 -2
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +13 -17
- data/lib/grape/api.rb +18 -13
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/headers.rb +5 -2
- data/lib/grape/dsl/helpers.rb +7 -5
- data/lib/grape/dsl/inside_route.rb +17 -8
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +3 -3
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +2 -2
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/endpoint.rb +21 -36
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/validation.rb +1 -2
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -1
- data/lib/grape/formatter/xml.rb +1 -0
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/auth/dsl.rb +7 -1
- data/lib/grape/middleware/base.rb +3 -1
- data/lib/grape/middleware/formatter.rb +4 -4
- data/lib/grape/middleware/stack.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +1 -0
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +2 -0
- data/lib/grape/parser/json.rb +1 -1
- data/lib/grape/parser/xml.rb +1 -1
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +3 -0
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +6 -0
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -2
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/params_scope.rb +88 -55
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +3 -3
- data/lib/grape/validations/validators/all_or_none.rb +8 -5
- data/lib/grape/validations/validators/allow_blank.rb +9 -7
- data/lib/grape/validations/validators/as.rb +6 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
- data/lib/grape/validations/validators/base.rb +75 -70
- data/lib/grape/validations/validators/coerce.rb +63 -79
- data/lib/grape/validations/validators/default.rb +37 -34
- data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
- data/lib/grape/validations/validators/except_values.rb +13 -11
- data/lib/grape/validations/validators/multiple_params_base.rb +24 -20
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
- data/lib/grape/validations/validators/presence.rb +7 -4
- data/lib/grape/validations/validators/regexp.rb +8 -5
- data/lib/grape/validations/validators/same_as.rb +18 -15
- data/lib/grape/validations/validators/values.rb +61 -56
- data/lib/grape/validations.rb +6 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +4 -1
- data/spec/grape/api/custom_validations_spec.rb +77 -45
- data/spec/grape/api/deeply_included_options_spec.rb +3 -3
- data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +1 -1
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
- data/spec/grape/api_remount_spec.rb +16 -15
- data/spec/grape/api_spec.rb +510 -220
- data/spec/grape/dsl/callbacks_spec.rb +2 -1
- data/spec/grape/dsl/headers_spec.rb +39 -9
- data/spec/grape/dsl/helpers_spec.rb +3 -2
- data/spec/grape/dsl/inside_route_spec.rb +6 -4
- data/spec/grape/dsl/logger_spec.rb +16 -18
- data/spec/grape/dsl/middleware_spec.rb +2 -1
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +10 -7
- data/spec/grape/endpoint/declared_spec.rb +259 -12
- data/spec/grape/endpoint_spec.rb +77 -55
- data/spec/grape/entity_spec.rb +22 -22
- data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
- data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
- data/spec/grape/exceptions/validation_spec.rb +5 -3
- data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
- data/spec/grape/integration/rack_sendfile_spec.rb +1 -1
- data/spec/grape/loading_spec.rb +8 -8
- data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
- data/spec/grape/middleware/auth/strategies_spec.rb +60 -20
- data/spec/grape/middleware/base_spec.rb +24 -15
- data/spec/grape/middleware/error_spec.rb +2 -2
- data/spec/grape/middleware/exception_spec.rb +111 -161
- data/spec/grape/middleware/formatter_spec.rb +27 -6
- data/spec/grape/middleware/globals_spec.rb +7 -4
- data/spec/grape/middleware/stack_spec.rb +14 -12
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
- data/spec/grape/middleware/versioner/header_spec.rb +14 -13
- data/spec/grape/middleware/versioner/param_spec.rb +7 -1
- data/spec/grape/middleware/versioner/path_spec.rb +5 -1
- data/spec/grape/middleware/versioner_spec.rb +1 -1
- data/spec/grape/parser_spec.rb +4 -0
- data/spec/grape/path_spec.rb +52 -52
- data/spec/grape/presenters/presenter_spec.rb +7 -6
- data/spec/grape/request_spec.rb +6 -4
- data/spec/grape/util/inheritable_setting_spec.rb +7 -7
- data/spec/grape/util/inheritable_values_spec.rb +3 -2
- data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
- data/spec/grape/util/stackable_values_spec.rb +7 -5
- data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +1 -0
- data/spec/grape/validations/params_scope_spec.rb +46 -10
- data/spec/grape/validations/single_attribute_iterator_spec.rb +2 -1
- data/spec/grape/validations/types/primitive_coercer_spec.rb +4 -4
- data/spec/grape/validations/types_spec.rb +8 -8
- data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
- data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
- data/spec/grape/validations/validators/coerce_spec.rb +99 -22
- data/spec/grape/validations/validators/default_spec.rb +72 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
- data/spec/grape/validations/validators/except_values_spec.rb +3 -3
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
- data/spec/grape/validations/validators/presence_spec.rb +16 -1
- data/spec/grape/validations/validators/regexp_spec.rb +25 -31
- data/spec/grape/validations/validators/same_as_spec.rb +14 -20
- data/spec/grape/validations/validators/values_spec.rb +183 -178
- data/spec/grape/validations_spec.rb +99 -58
- data/spec/integration/eager_load/eager_load_spec.rb +2 -2
- data/spec/integration/multi_json/json_spec.rb +1 -1
- data/spec/integration/multi_xml/xml_spec.rb +1 -1
- data/spec/shared/versioning_examples.rb +12 -9
- data/spec/spec_helper.rb +12 -2
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- metadata +102 -101
data/lib/grape/dsl/middleware.rb
CHANGED
@@ -18,28 +18,28 @@ module Grape
|
|
18
18
|
# to inject.
|
19
19
|
def use(middleware_class, *args, &block)
|
20
20
|
arr = [:use, middleware_class, *args]
|
21
|
-
arr << block if
|
21
|
+
arr << block if block
|
22
22
|
|
23
23
|
namespace_stackable(:middleware, arr)
|
24
24
|
end
|
25
25
|
|
26
26
|
def insert(*args, &block)
|
27
27
|
arr = [:insert, *args]
|
28
|
-
arr << block if
|
28
|
+
arr << block if block
|
29
29
|
|
30
30
|
namespace_stackable(:middleware, arr)
|
31
31
|
end
|
32
32
|
|
33
33
|
def insert_before(*args, &block)
|
34
34
|
arr = [:insert_before, *args]
|
35
|
-
arr << block if
|
35
|
+
arr << block if block
|
36
36
|
|
37
37
|
namespace_stackable(:middleware, arr)
|
38
38
|
end
|
39
39
|
|
40
40
|
def insert_after(*args, &block)
|
41
41
|
arr = [:insert_after, *args]
|
42
|
-
arr << block if
|
42
|
+
arr << block if block
|
43
43
|
|
44
44
|
namespace_stackable(:middleware, arr)
|
45
45
|
end
|
data/lib/grape/dsl/parameters.rb
CHANGED
@@ -133,7 +133,7 @@ module Grape
|
|
133
133
|
require_required_and_optional_fields(attrs.first, opts)
|
134
134
|
else
|
135
135
|
validate_attributes(attrs, opts, &block)
|
136
|
-
|
136
|
+
block ? new_scope(orig_attrs, &block) : push_declared_params(attrs, **opts.slice(:as))
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
@@ -149,7 +149,7 @@ module Grape
|
|
149
149
|
opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
|
150
150
|
|
151
151
|
# check type for optional parameter group
|
152
|
-
if attrs &&
|
152
|
+
if attrs && block
|
153
153
|
raise Grape::Exceptions::MissingGroupTypeError.new if type.nil?
|
154
154
|
raise Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
|
155
155
|
end
|
@@ -159,7 +159,7 @@ module Grape
|
|
159
159
|
else
|
160
160
|
validate_attributes(attrs, opts, &block)
|
161
161
|
|
162
|
-
|
162
|
+
block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
@@ -26,6 +26,7 @@ module Grape
|
|
26
26
|
# define a single mime type
|
27
27
|
mime_type = content_types[new_format.to_sym]
|
28
28
|
raise Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type
|
29
|
+
|
29
30
|
namespace_stackable(:content_types, new_format.to_sym => mime_type)
|
30
31
|
else
|
31
32
|
namespace_inheritable(:format)
|
@@ -102,14 +103,13 @@ module Grape
|
|
102
103
|
def rescue_from(*args, &block)
|
103
104
|
if args.last.is_a?(Proc)
|
104
105
|
handler = args.pop
|
105
|
-
elsif
|
106
|
+
elsif block
|
106
107
|
handler = block
|
107
108
|
end
|
108
109
|
|
109
110
|
options = args.extract_options!
|
110
|
-
if
|
111
|
-
|
112
|
-
end
|
111
|
+
raise ArgumentError, 'both :with option and block cannot be passed' if block && options.key?(:with)
|
112
|
+
|
113
113
|
handler ||= extract_with(options)
|
114
114
|
|
115
115
|
if args.include?(:all)
|
@@ -127,7 +127,7 @@ module Grape
|
|
127
127
|
:base_only_rescue_handlers
|
128
128
|
end
|
129
129
|
|
130
|
-
namespace_reverse_stackable handler_type,
|
130
|
+
namespace_reverse_stackable handler_type, args.map { |arg| [arg, handler] }.to_h
|
131
131
|
end
|
132
132
|
|
133
133
|
namespace_stackable(:rescue_options, options)
|
@@ -154,7 +154,8 @@ module Grape
|
|
154
154
|
# @param model_class [Class] The model class that will be represented.
|
155
155
|
# @option options [Class] :with The entity class that will represent the model.
|
156
156
|
def represent(model_class, options)
|
157
|
-
raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with]
|
157
|
+
raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with].is_a?(Class)
|
158
|
+
|
158
159
|
namespace_stackable(:representations, model_class => options[:with])
|
159
160
|
end
|
160
161
|
|
@@ -162,9 +163,11 @@ module Grape
|
|
162
163
|
|
163
164
|
def extract_with(options)
|
164
165
|
return unless options.key?(:with)
|
166
|
+
|
165
167
|
with_option = options.delete(:with)
|
166
168
|
return with_option if with_option.instance_of?(Proc)
|
167
169
|
return with_option.to_sym if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
|
170
|
+
|
168
171
|
raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
|
169
172
|
end
|
170
173
|
end
|
data/lib/grape/dsl/routing.rb
CHANGED
@@ -38,7 +38,7 @@ module Grape
|
|
38
38
|
|
39
39
|
@versions = versions | requested_versions
|
40
40
|
|
41
|
-
if
|
41
|
+
if block
|
42
42
|
within_namespace do
|
43
43
|
namespace_inheritable(:version, requested_versions)
|
44
44
|
namespace_inheritable(:version_options, options)
|
@@ -166,7 +166,7 @@ module Grape
|
|
166
166
|
def namespace(space = nil, options = {}, &block)
|
167
167
|
@namespace_description = nil unless instance_variable_defined?(:@namespace_description) && @namespace_description
|
168
168
|
|
169
|
-
if space ||
|
169
|
+
if space || block
|
170
170
|
within_namespace do
|
171
171
|
previous_namespace_description = @namespace_description
|
172
172
|
@namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
|
data/lib/grape/dsl/settings.rb
CHANGED
@@ -103,12 +103,14 @@ module Grape
|
|
103
103
|
def namespace_stackable_with_hash(key)
|
104
104
|
settings = get_or_set :namespace_stackable, key, nil
|
105
105
|
return if settings.blank?
|
106
|
+
|
106
107
|
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
|
107
108
|
end
|
108
109
|
|
109
110
|
def namespace_reverse_stackable_with_hash(key)
|
110
111
|
settings = get_or_set :namespace_reverse_stackable, key, nil
|
111
112
|
return if settings.blank?
|
113
|
+
|
112
114
|
result = {}
|
113
115
|
settings.each do |setting|
|
114
116
|
setting.each do |field, value|
|
@@ -154,10 +156,10 @@ module Grape
|
|
154
156
|
|
155
157
|
# Execute the block within a context where our inheritable settings are forked
|
156
158
|
# to a new copy (see #namespace_start).
|
157
|
-
def within_namespace(&
|
159
|
+
def within_namespace(&block)
|
158
160
|
namespace_start
|
159
161
|
|
160
|
-
result = yield if
|
162
|
+
result = yield if block
|
161
163
|
|
162
164
|
namespace_end
|
163
165
|
reset_validations!
|
@@ -175,9 +177,7 @@ module Grape
|
|
175
177
|
# +inheritable_setting+, however, it doesn't contain any user-defined settings.
|
176
178
|
# Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
|
177
179
|
# in the chain for every endpoint.
|
178
|
-
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
179
|
-
setting.inherit_from superclass.inheritable_setting
|
180
|
-
end
|
180
|
+
setting.inherit_from superclass.inheritable_setting if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
181
181
|
end
|
182
182
|
end
|
183
183
|
end
|
data/lib/grape/endpoint.rb
CHANGED
@@ -20,7 +20,8 @@ module Grape
|
|
20
20
|
def before_each(new_setup = false, &block)
|
21
21
|
@before_each ||= []
|
22
22
|
if new_setup == false
|
23
|
-
return @before_each unless
|
23
|
+
return @before_each unless block
|
24
|
+
|
24
25
|
@before_each << block
|
25
26
|
else
|
26
27
|
@before_each = [new_setup]
|
@@ -46,9 +47,7 @@ module Grape
|
|
46
47
|
# @return [Proc]
|
47
48
|
# @raise [NameError] an instance method with the same name already exists
|
48
49
|
def generate_api_method(method_name, &block)
|
49
|
-
if method_defined?(method_name)
|
50
|
-
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
|
51
|
-
end
|
50
|
+
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") if method_defined?(method_name)
|
52
51
|
|
53
52
|
define_method(method_name, &block)
|
54
53
|
method = instance_method(method_name)
|
@@ -106,7 +105,7 @@ module Grape
|
|
106
105
|
@body = nil
|
107
106
|
@proc = nil
|
108
107
|
|
109
|
-
return unless
|
108
|
+
return unless block
|
110
109
|
|
111
110
|
@source = block
|
112
111
|
@block = self.class.generate_api_method(method_name, &block)
|
@@ -118,11 +117,9 @@ module Grape
|
|
118
117
|
inheritable_setting.route[:saved_validations] += namespace_stackable[:validations]
|
119
118
|
parent_declared_params = namespace_stackable[:declared_params]
|
120
119
|
|
121
|
-
if parent_declared_params
|
122
|
-
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten)
|
123
|
-
end
|
120
|
+
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
|
124
121
|
|
125
|
-
endpoints
|
122
|
+
endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
|
126
123
|
end
|
127
124
|
|
128
125
|
def require_option(options, key)
|
@@ -142,7 +139,7 @@ module Grape
|
|
142
139
|
end
|
143
140
|
|
144
141
|
def reset_routes!
|
145
|
-
endpoints
|
142
|
+
endpoints&.each(&:reset_routes!)
|
146
143
|
@namespace = nil
|
147
144
|
@routes = nil
|
148
145
|
end
|
@@ -154,13 +151,9 @@ module Grape
|
|
154
151
|
reset_routes!
|
155
152
|
routes.each do |route|
|
156
153
|
methods = [route.request_method]
|
157
|
-
if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
|
158
|
-
methods << Grape::Http::Headers::HEAD
|
159
|
-
end
|
154
|
+
methods << Grape::Http::Headers::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
|
160
155
|
methods.each do |method|
|
161
|
-
unless route.request_method == method
|
162
|
-
route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h)
|
163
|
-
end
|
156
|
+
route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h) unless route.request_method == method
|
164
157
|
router.append(route.apply(self))
|
165
158
|
end
|
166
159
|
end
|
@@ -200,6 +193,7 @@ module Grape
|
|
200
193
|
def prepare_version
|
201
194
|
version = namespace_inheritable(:version) || []
|
202
195
|
return if version.empty?
|
196
|
+
|
203
197
|
version.length == 1 ? version.first.to_s : version
|
204
198
|
end
|
205
199
|
|
@@ -234,7 +228,7 @@ module Grape
|
|
234
228
|
# Return the collection of endpoints within this endpoint.
|
235
229
|
# This is the case when an Grape::API mounts another Grape::API.
|
236
230
|
def endpoints
|
237
|
-
options[:app].endpoints if options[:app]
|
231
|
+
options[:app].endpoints if options[:app].respond_to?(:endpoints)
|
238
232
|
end
|
239
233
|
|
240
234
|
def equals?(e)
|
@@ -255,14 +249,14 @@ module Grape
|
|
255
249
|
run_filters befores, :before
|
256
250
|
|
257
251
|
if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
|
258
|
-
raise Grape::Exceptions::MethodNotAllowed
|
252
|
+
raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allowed_methods)) unless options?
|
253
|
+
|
259
254
|
header 'Allow', allowed_methods
|
260
255
|
response_object = ''
|
261
256
|
status 204
|
262
257
|
else
|
263
258
|
run_filters before_validations, :before_validation
|
264
259
|
run_validators validations, request
|
265
|
-
remove_renamed_params
|
266
260
|
run_filters after_validations, :after_validation
|
267
261
|
response_object = execute
|
268
262
|
end
|
@@ -328,14 +322,7 @@ module Grape
|
|
328
322
|
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
329
323
|
end
|
330
324
|
|
331
|
-
|
332
|
-
return unless route_setting(:renamed_params)
|
333
|
-
route_setting(:renamed_params).flat_map(&:keys).each do |renamed_param|
|
334
|
-
@params.delete(renamed_param)
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
private :build_stack, :build_helpers, :remove_renamed_params
|
325
|
+
private :build_stack, :build_helpers
|
339
326
|
|
340
327
|
def execute
|
341
328
|
@block ? @block.call(self) : nil
|
@@ -365,15 +352,13 @@ module Grape
|
|
365
352
|
|
366
353
|
ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do
|
367
354
|
validators.each do |validator|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
break if validator.fail_fast?
|
376
|
-
end
|
355
|
+
validator.validate(request)
|
356
|
+
rescue Grape::Exceptions::Validation => e
|
357
|
+
validation_errors << e
|
358
|
+
break if validator.fail_fast?
|
359
|
+
rescue Grape::Exceptions::ValidationArrayErrors => e
|
360
|
+
validation_errors.concat e.errors
|
361
|
+
break if validator.fail_fast?
|
377
362
|
end
|
378
363
|
end
|
379
364
|
|
@@ -10,12 +10,8 @@ module Grape
|
|
10
10
|
result = wrap_message(present(message, env))
|
11
11
|
|
12
12
|
rescue_options = options[:rescue_options] || {}
|
13
|
-
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
14
|
-
|
15
|
-
end
|
16
|
-
if rescue_options[:original_exception] && original_exception
|
17
|
-
result = result.merge(original_exception: original_exception.inspect)
|
18
|
-
end
|
13
|
+
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
14
|
+
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
19
15
|
::Grape::Json.dump(result)
|
20
16
|
end
|
21
17
|
|
@@ -11,12 +11,8 @@ module Grape
|
|
11
11
|
|
12
12
|
result = message.is_a?(Hash) ? message : { message: message }
|
13
13
|
rescue_options = options[:rescue_options] || {}
|
14
|
-
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
15
|
-
|
16
|
-
end
|
17
|
-
if rescue_options[:original_exception] && original_exception
|
18
|
-
result = result.merge(original_exception: original_exception.inspect)
|
19
|
-
end
|
14
|
+
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
15
|
+
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
20
16
|
result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
|
21
17
|
end
|
22
18
|
end
|
@@ -5,8 +5,7 @@ require 'grape/exceptions/base'
|
|
5
5
|
module Grape
|
6
6
|
module Exceptions
|
7
7
|
class Validation < Grape::Exceptions::Base
|
8
|
-
attr_accessor :params
|
9
|
-
attr_accessor :message_key
|
8
|
+
attr_accessor :params, :message_key
|
10
9
|
|
11
10
|
def initialize(params:, message: nil, **args)
|
12
11
|
@params = params
|
data/lib/grape/formatter/json.rb
CHANGED
@@ -8,13 +8,14 @@ module Grape
|
|
8
8
|
return object if object.is_a?(String)
|
9
9
|
return ::Grape::Json.dump(serialize(object)) if serializable?(object)
|
10
10
|
return object.to_json if object.respond_to?(:to_json)
|
11
|
+
|
11
12
|
::Grape::Json.dump(object)
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
15
16
|
|
16
17
|
def serializable?(object)
|
17
|
-
object.respond_to?(:serializable_hash) || object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash } || object.is_a?(Hash)
|
18
|
+
object.respond_to?(:serializable_hash) || (object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash }) || object.is_a?(Hash)
|
18
19
|
end
|
19
20
|
|
20
21
|
def serialize(object)
|
data/lib/grape/formatter/xml.rb
CHANGED
data/lib/grape/locale/en.yml
CHANGED
@@ -44,6 +44,7 @@ en:
|
|
44
44
|
"when specifying %{body_format} as content-type, you must pass valid
|
45
45
|
%{body_format} in the request's 'body'
|
46
46
|
"
|
47
|
+
empty_message_body: 'Empty message body supplied with %{body_format} content-type'
|
47
48
|
invalid_accept_header:
|
48
49
|
problem: 'Invalid accept header'
|
49
50
|
resolution: '%{message}'
|
@@ -51,4 +52,3 @@ en:
|
|
51
52
|
problem: 'Invalid version header'
|
52
53
|
resolution: '%{message}'
|
53
54
|
invalid_response: 'Invalid response'
|
54
|
-
|
@@ -32,7 +32,13 @@ module Grape
|
|
32
32
|
|
33
33
|
def http_digest(options = {}, &block)
|
34
34
|
options[:realm] ||= 'API Authorization'
|
35
|
-
|
35
|
+
|
36
|
+
if options[:realm].respond_to?(:values_at)
|
37
|
+
options[:realm][:opaque] ||= 'secret'
|
38
|
+
else
|
39
|
+
options[:opaque] ||= 'secret'
|
40
|
+
end
|
41
|
+
|
36
42
|
auth :http_digest, options, &block
|
37
43
|
end
|
38
44
|
end
|
@@ -59,7 +59,8 @@ module Grape
|
|
59
59
|
|
60
60
|
def response
|
61
61
|
return @app_response if @app_response.is_a?(Rack::Response)
|
62
|
-
|
62
|
+
|
63
|
+
@app_response = Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
|
63
64
|
end
|
64
65
|
|
65
66
|
def content_type_for(format)
|
@@ -84,6 +85,7 @@ module Grape
|
|
84
85
|
|
85
86
|
def merge_headers(response)
|
86
87
|
return unless headers.is_a?(Hash)
|
88
|
+
|
87
89
|
case response
|
88
90
|
when Rack::Response then response.headers.merge!(headers)
|
89
91
|
when Array then response[1].merge!(headers)
|
@@ -22,6 +22,7 @@ module Grape
|
|
22
22
|
|
23
23
|
def after
|
24
24
|
return unless @app_response
|
25
|
+
|
25
26
|
status, headers, bodies = *@app_response
|
26
27
|
|
27
28
|
if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
|
@@ -79,7 +80,7 @@ module Grape
|
|
79
80
|
(request.post? || request.put? || request.patch? || request.delete?) &&
|
80
81
|
(!request.form_data? || !request.media_type) &&
|
81
82
|
!request.parseable_data? &&
|
82
|
-
(request.content_length.to_i
|
83
|
+
(request.content_length.to_i.positive? || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == CHUNKED)
|
83
84
|
|
84
85
|
return unless (input = env[Grape::Env::RACK_INPUT])
|
85
86
|
|
@@ -96,9 +97,7 @@ module Grape
|
|
96
97
|
def read_rack_input(body)
|
97
98
|
fmt = request.media_type ? mime_types[request.media_type] : options[:default_format]
|
98
99
|
|
99
|
-
unless content_type_for(fmt)
|
100
|
-
throw :error, status: 415, message: "The provided content-type '#{request.media_type}' is not supported."
|
101
|
-
end
|
100
|
+
throw :error, status: 415, message: "The provided content-type '#{request.media_type}' is not supported." unless content_type_for(fmt)
|
102
101
|
parser = Grape::Parser.parser_for fmt, **options
|
103
102
|
if parser
|
104
103
|
begin
|
@@ -145,6 +144,7 @@ module Grape
|
|
145
144
|
fmt = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[Grape::Http::Headers::FORMAT]
|
146
145
|
# avoid symbol memory leak on an unknown format
|
147
146
|
return fmt.to_sym if content_type_for(fmt)
|
147
|
+
|
148
148
|
fmt
|
149
149
|
end
|
150
150
|
|
@@ -22,11 +22,9 @@ module Grape
|
|
22
22
|
def before
|
23
23
|
potential_version = (env[Grape::Http::Headers::HTTP_ACCEPT_VERSION] || '').strip
|
24
24
|
|
25
|
-
if strict?
|
25
|
+
if strict? && potential_version.empty?
|
26
26
|
# If no Accept-Version header:
|
27
|
-
|
28
|
-
throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
|
29
|
-
end
|
27
|
+
throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
|
30
28
|
end
|
31
29
|
|
32
30
|
return if potential_version.empty?
|
@@ -51,7 +49,7 @@ module Grape
|
|
51
49
|
# of routes (see Grape::Router) for more information). To prevent
|
52
50
|
# this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
|
53
51
|
def cascade?
|
54
|
-
if options[:version_options]
|
52
|
+
if options[:version_options]&.key?(:cascade)
|
55
53
|
options[:version_options][:cascade]
|
56
54
|
else
|
57
55
|
true
|
@@ -26,10 +26,10 @@ module Grape
|
|
26
26
|
# route.
|
27
27
|
class Header < Base
|
28
28
|
VENDOR_VERSION_HEADER_REGEX =
|
29
|
-
/\Avnd\.([a-z0-9.\-_
|
29
|
+
/\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/.freeze
|
30
30
|
|
31
|
-
HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_
|
32
|
-
HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_
|
31
|
+
HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_!#{Regexp.last_match(0)}\^]+/.freeze
|
32
|
+
HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))+/.freeze
|
33
33
|
|
34
34
|
def before
|
35
35
|
strict_header_checks if strict?
|
@@ -52,12 +52,14 @@ module Grape
|
|
52
52
|
|
53
53
|
def strict_accept_header_presence_check
|
54
54
|
return unless header.qvalues.empty?
|
55
|
+
|
55
56
|
fail_with_invalid_accept_header!('Accept header must be set.')
|
56
57
|
end
|
57
58
|
|
58
59
|
def strict_version_vendor_accept_header_presence_check
|
59
60
|
return unless versions.present?
|
60
61
|
return if an_accept_header_with_version_and_vendor_is_present?
|
62
|
+
|
61
63
|
fail_with_invalid_accept_header!('API vendor or version not found.')
|
62
64
|
end
|
63
65
|
|
@@ -160,7 +162,7 @@ module Grape
|
|
160
162
|
# information). To prevent # this behavior, and not add the `X-Cascade`
|
161
163
|
# header, one can set the `:cascade` option to `false`.
|
162
164
|
def cascade?
|
163
|
-
if version_options
|
165
|
+
if version_options&.key?(:cascade)
|
164
166
|
version_options[:cascade]
|
165
167
|
else
|
166
168
|
true
|
@@ -32,6 +32,7 @@ module Grape
|
|
32
32
|
def before
|
33
33
|
potential_version = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[paramkey]
|
34
34
|
return if potential_version.nil?
|
35
|
+
|
35
36
|
throw :error, status: 404, message: '404 API Version Not Found', headers: { Grape::Http::Headers::X_CASCADE => 'pass' } if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
36
37
|
env[Grape::Env::API_VERSION] = potential_version
|
37
38
|
env[Grape::Env::RACK_REQUEST_QUERY_HASH].delete(paramkey) if env.key? Grape::Env::RACK_REQUEST_QUERY_HASH
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'English'
|
3
4
|
module Rack
|
4
5
|
module Accept
|
5
6
|
module Header
|
6
|
-
ALLOWED_CHARACTERS = %r{^([a-z*]+)
|
7
|
+
ALLOWED_CHARACTERS = %r{^([a-z*]+)/([a-z0-9*&\^\-_#{$ERROR_INFO}.+]+)(?:;([a-z0-9=;]+))?$}.freeze
|
7
8
|
class << self
|
8
9
|
# Corrected version of https://github.com/mjackson/rack-accept/blob/master/lib/rack/accept/header.rb#L40-L44
|
9
10
|
def parse_media_type(media_type)
|
@@ -37,6 +37,7 @@ module Grape
|
|
37
37
|
pieces = path.split('/')
|
38
38
|
potential_version = pieces[1]
|
39
39
|
return unless potential_version&.match?(options[:pattern])
|
40
|
+
|
40
41
|
throw :error, status: 404, message: '404 API Version Not Found' if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
41
42
|
env[Grape::Env::API_VERSION] = potential_version
|
42
43
|
end
|
@@ -45,6 +46,7 @@ module Grape
|
|
45
46
|
|
46
47
|
def mounted_path?(path)
|
47
48
|
return false unless mount_path && path.start_with?(mount_path)
|
49
|
+
|
48
50
|
rest = path.slice(mount_path.length..-1)
|
49
51
|
rest.start_with?('/') || rest.empty?
|
50
52
|
end
|
data/lib/grape/parser/json.rb
CHANGED
@@ -8,7 +8,7 @@ module Grape
|
|
8
8
|
::Grape::Json.load(object)
|
9
9
|
rescue ::Grape::Json::ParseError
|
10
10
|
# handle JSON parsing errors via the rescue handlers or provide error message
|
11
|
-
raise Grape::Exceptions::InvalidMessageBody
|
11
|
+
raise Grape::Exceptions::InvalidMessageBody.new('application/json')
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/grape/parser/xml.rb
CHANGED
@@ -8,7 +8,7 @@ module Grape
|
|
8
8
|
::Grape::Xml.parse(object)
|
9
9
|
rescue ::Grape::Xml::ParseError
|
10
10
|
# handle XML parsing errors via the rescue handlers or provide error message
|
11
|
-
raise Grape::Exceptions::InvalidMessageBody
|
11
|
+
raise Grape::Exceptions::InvalidMessageBody.new('application/xml')
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/grape/path.rb
CHANGED
data/lib/grape/request.rb
CHANGED
@@ -15,6 +15,8 @@ module Grape
|
|
15
15
|
|
16
16
|
def params
|
17
17
|
@params ||= build_params
|
18
|
+
rescue EOFError
|
19
|
+
raise Grape::Exceptions::EmptyMessageBody.new(content_type)
|
18
20
|
end
|
19
21
|
|
20
22
|
def headers
|
@@ -35,6 +37,7 @@ module Grape
|
|
35
37
|
Grape::Util::LazyObject.new do
|
36
38
|
env.each_pair.with_object({}) do |(k, v), headers|
|
37
39
|
next unless k.to_s.start_with? HTTP_PREFIX
|
40
|
+
|
38
41
|
transformed_header = Grape::Http::Headers::HTTP_HEADERS[k] || transform_header(k)
|
39
42
|
headers[transformed_header] = v
|
40
43
|
end
|
data/lib/grape/router/pattern.rb
CHANGED