grape 2.0.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +151 -1
- data/CONTRIBUTING.md +1 -1
- data/README.md +404 -334
- data/UPGRADING.md +279 -7
- data/grape.gemspec +8 -8
- data/lib/grape/api/instance.rb +34 -66
- data/lib/grape/api.rb +47 -70
- data/lib/grape/content_types.rb +13 -10
- data/lib/grape/cookies.rb +31 -24
- data/lib/grape/dry_types.rb +0 -2
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/desc.rb +49 -44
- data/lib/grape/dsl/headers.rb +2 -2
- data/lib/grape/dsl/helpers.rb +8 -4
- data/lib/grape/dsl/inside_route.rb +67 -54
- data/lib/grape/dsl/parameters.rb +10 -9
- data/lib/grape/dsl/request_response.rb +14 -18
- data/lib/grape/dsl/routing.rb +34 -17
- data/lib/grape/dsl/validations.rb +13 -0
- data/lib/grape/endpoint.rb +120 -118
- data/lib/grape/{util/env.rb → env.rb} +0 -5
- data/lib/grape/error_formatter/base.rb +51 -21
- data/lib/grape/error_formatter/json.rb +7 -15
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +11 -17
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +5 -25
- data/lib/grape/exceptions/base.rb +18 -30
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/validation.rb +5 -6
- data/lib/grape/exceptions/validation_array_errors.rb +1 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -6
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
- data/lib/grape/extensions/hash.rb +7 -2
- data/lib/grape/extensions/hashie/mash.rb +3 -5
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +4 -6
- data/lib/grape/formatter/serializable_hash.rb +1 -1
- data/lib/grape/formatter/txt.rb +3 -5
- data/lib/grape/formatter/xml.rb +4 -6
- data/lib/grape/formatter.rb +7 -25
- data/lib/grape/{util/json.rb → json.rb} +1 -3
- data/lib/grape/locale/en.yml +46 -42
- data/lib/grape/middleware/auth/base.rb +11 -34
- data/lib/grape/middleware/auth/dsl.rb +23 -31
- data/lib/grape/middleware/base.rb +41 -23
- data/lib/grape/middleware/error.rb +77 -76
- data/lib/grape/middleware/formatter.rb +48 -79
- data/lib/grape/middleware/globals.rb +1 -3
- data/lib/grape/middleware/stack.rb +26 -37
- data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
- data/lib/grape/middleware/versioner/base.rb +74 -0
- data/lib/grape/middleware/versioner/header.rb +59 -126
- data/lib/grape/middleware/versioner/param.rb +4 -25
- data/lib/grape/middleware/versioner/path.rb +10 -34
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/namespace.rb +4 -5
- data/lib/grape/params_builder/base.rb +18 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -23
- data/lib/grape/path.rb +38 -60
- data/lib/grape/request.rb +161 -30
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +45 -31
- data/lib/grape/router/route.rb +28 -57
- data/lib/grape/router.rb +56 -43
- data/lib/grape/util/base_inheritable.rb +4 -4
- data/lib/grape/util/cache.rb +0 -3
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_values.rb +0 -2
- data/lib/grape/util/lazy/block.rb +29 -0
- data/lib/grape/util/lazy/value.rb +38 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +34 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +70 -0
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/util/reverse_stackable_values.rb +1 -6
- data/lib/grape/util/stackable_values.rb +1 -6
- data/lib/grape/util/strict_hash_configuration.rb +3 -3
- data/lib/grape/validations/attributes_doc.rb +38 -36
- data/lib/grape/validations/attributes_iterator.rb +1 -0
- data/lib/grape/validations/contract_scope.rb +34 -0
- data/lib/grape/validations/params_scope.rb +36 -32
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +9 -15
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +0 -2
- data/lib/grape/validations/types/set_coercer.rb +0 -3
- data/lib/grape/validations/types.rb +0 -3
- data/lib/grape/validations/validator_factory.rb +2 -2
- data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
- data/lib/grape/validations/validators/base.rb +8 -11
- data/lib/grape/validations/validators/coerce_validator.rb +1 -1
- data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
- data/lib/grape/validations/validators/default_validator.rb +6 -2
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +1 -1
- data/lib/grape/validations/validators/except_values_validator.rb +2 -2
- data/lib/grape/validations/validators/length_validator.rb +49 -0
- data/lib/grape/validations/validators/presence_validator.rb +1 -1
- data/lib/grape/validations/validators/regexp_validator.rb +2 -2
- data/lib/grape/validations/validators/values_validator.rb +20 -57
- data/lib/grape/validations.rb +8 -21
- data/lib/grape/version.rb +1 -1
- data/lib/grape/{util/xml.rb → xml.rb} +1 -1
- data/lib/grape.rb +42 -274
- metadata +45 -44
- data/lib/grape/eager_load.rb +0 -20
- data/lib/grape/http/headers.rb +0 -71
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
- data/lib/grape/router/attribute_translator.rb +0 -63
- data/lib/grape/util/lazy_block.rb +0 -27
- data/lib/grape/util/lazy_object.rb +0 -43
- data/lib/grape/util/lazy_value.rb +0 -91
- data/lib/grape/util/registrable.rb +0 -15
- data/lib/grape/validations/types/build_coercer.rb +0 -94
data/lib/grape/endpoint.rb
CHANGED
@@ -6,15 +6,19 @@ module Grape
|
|
6
6
|
# on the instance level of this class may be called
|
7
7
|
# from inside a `get`, `post`, etc.
|
8
8
|
class Endpoint
|
9
|
+
extend Forwardable
|
9
10
|
include Grape::DSL::Settings
|
10
11
|
include Grape::DSL::InsideRoute
|
11
12
|
|
12
13
|
attr_accessor :block, :source, :options
|
13
|
-
attr_reader :env, :request
|
14
|
+
attr_reader :env, :request
|
15
|
+
|
16
|
+
def_delegators :request, :params, :headers, :cookies
|
17
|
+
def_delegator :cookies, :response_cookies
|
14
18
|
|
15
19
|
class << self
|
16
|
-
def new(
|
17
|
-
self == Endpoint ? Class.new(Endpoint).new(
|
20
|
+
def new(...)
|
21
|
+
self == Endpoint ? Class.new(Endpoint).new(...) : super
|
18
22
|
end
|
19
23
|
|
20
24
|
def before_each(new_setup = false, &block)
|
@@ -30,7 +34,7 @@ module Grape
|
|
30
34
|
|
31
35
|
def run_before_each(endpoint)
|
32
36
|
superclass.run_before_each(endpoint) unless self == Endpoint
|
33
|
-
before_each.each { |blk| blk.
|
37
|
+
before_each.each { |blk| blk.try(:call, endpoint) }
|
34
38
|
end
|
35
39
|
|
36
40
|
# @api private
|
@@ -55,7 +59,7 @@ module Grape
|
|
55
59
|
|
56
60
|
proc do |endpoint_instance|
|
57
61
|
ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: endpoint_instance) do
|
58
|
-
method.
|
62
|
+
method.bind_call(endpoint_instance)
|
59
63
|
end
|
60
64
|
end
|
61
65
|
end
|
@@ -114,10 +118,10 @@ module Grape
|
|
114
118
|
# Update our settings from a given set of stackable parameters. Used when
|
115
119
|
# the endpoint's API is mounted under another one.
|
116
120
|
def inherit_settings(namespace_stackable)
|
117
|
-
|
121
|
+
parent_validations = namespace_stackable[:validations]
|
122
|
+
inheritable_setting.route[:saved_validations].concat(parent_validations) if parent_validations.any?
|
118
123
|
parent_declared_params = namespace_stackable[:declared_params]
|
119
|
-
|
120
|
-
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
|
124
|
+
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params.any?
|
121
125
|
|
122
126
|
endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
|
123
127
|
end
|
@@ -135,7 +139,7 @@ module Grape
|
|
135
139
|
end
|
136
140
|
|
137
141
|
def routes
|
138
|
-
@routes ||= endpoints
|
142
|
+
@routes ||= endpoints&.collect(&:routes)&.flatten || to_routes
|
139
143
|
end
|
140
144
|
|
141
145
|
def reset_routes!
|
@@ -145,27 +149,27 @@ module Grape
|
|
145
149
|
end
|
146
150
|
|
147
151
|
def mount_in(router)
|
148
|
-
if endpoints
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
end
|
152
|
+
return endpoints.each { |e| e.mount_in(router) } if endpoints
|
153
|
+
|
154
|
+
reset_routes!
|
155
|
+
routes.each do |route|
|
156
|
+
router.append(route.apply(self))
|
157
|
+
next unless !namespace_inheritable(:do_not_route_head) && route.request_method == Rack::GET
|
158
|
+
|
159
|
+
route.dup.then do |head_route|
|
160
|
+
head_route.convert_to_head_request!
|
161
|
+
router.append(head_route.apply(self))
|
159
162
|
end
|
160
163
|
end
|
161
164
|
end
|
162
165
|
|
163
166
|
def to_routes
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
167
|
+
default_route_options = prepare_default_route_attributes
|
168
|
+
|
169
|
+
map_routes do |method, raw_path|
|
170
|
+
prepared_path = Path.new(raw_path, namespace, prepare_default_path_settings)
|
171
|
+
params = options[:route_options].present? ? options[:route_options].merge(default_route_options) : default_route_options
|
172
|
+
route = Grape::Router::Route.new(method, prepared_path.origin, prepared_path.suffix, params)
|
169
173
|
route.apply(self)
|
170
174
|
end.flatten
|
171
175
|
end
|
@@ -190,23 +194,20 @@ module Grape
|
|
190
194
|
end
|
191
195
|
|
192
196
|
def prepare_version
|
193
|
-
version = namespace_inheritable(:version)
|
194
|
-
return if version.
|
197
|
+
version = namespace_inheritable(:version)
|
198
|
+
return if version.blank?
|
195
199
|
|
196
|
-
version.length == 1 ? version.first
|
197
|
-
end
|
198
|
-
|
199
|
-
def merge_route_options(**default)
|
200
|
-
options[:route_options].clone.merge!(**default)
|
200
|
+
version.length == 1 ? version.first : version
|
201
201
|
end
|
202
202
|
|
203
203
|
def map_routes
|
204
204
|
options[:method].map { |method| options[:path].map { |path| yield method, path } }
|
205
205
|
end
|
206
206
|
|
207
|
-
def
|
208
|
-
|
209
|
-
|
207
|
+
def prepare_default_path_settings
|
208
|
+
namespace_stackable_hash = inheritable_setting.namespace_stackable.to_hash
|
209
|
+
namespace_inheritable_hash = inheritable_setting.namespace_inheritable.to_hash
|
210
|
+
namespace_stackable_hash.merge!(namespace_inheritable_hash)
|
210
211
|
end
|
211
212
|
|
212
213
|
def namespace
|
@@ -227,30 +228,36 @@ module Grape
|
|
227
228
|
# Return the collection of endpoints within this endpoint.
|
228
229
|
# This is the case when an Grape::API mounts another Grape::API.
|
229
230
|
def endpoints
|
230
|
-
|
231
|
+
@endpoints ||= options[:app].try(:endpoints)
|
232
|
+
end
|
233
|
+
|
234
|
+
def equals?(endpoint)
|
235
|
+
(options == endpoint.options) && (inheritable_setting.to_hash == endpoint.inheritable_setting.to_hash)
|
231
236
|
end
|
232
237
|
|
233
|
-
|
234
|
-
|
238
|
+
# The purpose of this override is solely for stripping internals when an error occurs while calling
|
239
|
+
# an endpoint through an api. See https://github.com/ruby-grape/grape/issues/2398
|
240
|
+
# Otherwise, it calls super.
|
241
|
+
def inspect
|
242
|
+
return super unless env
|
243
|
+
|
244
|
+
"#{self.class} in '#{route.origin}' endpoint"
|
235
245
|
end
|
236
246
|
|
237
247
|
protected
|
238
248
|
|
239
249
|
def run
|
240
250
|
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
|
241
|
-
@header = {}
|
242
251
|
@request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with))
|
243
|
-
@params = @request.params
|
244
|
-
@headers = @request.headers
|
245
252
|
begin
|
246
|
-
cookies.read(@request)
|
247
253
|
self.class.run_before_each(self)
|
248
254
|
run_filters befores, :before
|
249
255
|
|
250
|
-
if (
|
251
|
-
|
256
|
+
if env.key?(Grape::Env::GRAPE_ALLOWED_METHODS)
|
257
|
+
header['Allow'] = env[Grape::Env::GRAPE_ALLOWED_METHODS].join(', ')
|
258
|
+
raise Grape::Exceptions::MethodNotAllowed.new(header) unless options?
|
252
259
|
|
253
|
-
header
|
260
|
+
header 'Allow', header['Allow']
|
254
261
|
response_object = ''
|
255
262
|
status 204
|
256
263
|
else
|
@@ -261,7 +268,7 @@ module Grape
|
|
261
268
|
end
|
262
269
|
|
263
270
|
run_filters afters, :after
|
264
|
-
|
271
|
+
build_response_cookies
|
265
272
|
|
266
273
|
# status verifies body presence when DELETE
|
267
274
|
@body ||= response_object
|
@@ -276,54 +283,6 @@ module Grape
|
|
276
283
|
end
|
277
284
|
end
|
278
285
|
|
279
|
-
def build_stack(helpers)
|
280
|
-
stack = Grape::Middleware::Stack.new
|
281
|
-
|
282
|
-
stack.use Rack::Head
|
283
|
-
stack.use Class.new(Grape::Middleware::Error),
|
284
|
-
helpers: helpers,
|
285
|
-
format: namespace_inheritable(:format),
|
286
|
-
content_types: namespace_stackable_with_hash(:content_types),
|
287
|
-
default_status: namespace_inheritable(:default_error_status),
|
288
|
-
rescue_all: namespace_inheritable(:rescue_all),
|
289
|
-
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
290
|
-
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
291
|
-
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
292
|
-
rescue_options: namespace_stackable_with_hash(:rescue_options) || {},
|
293
|
-
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers) || {},
|
294
|
-
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {},
|
295
|
-
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
|
296
|
-
grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
|
297
|
-
|
298
|
-
stack.concat namespace_stackable(:middleware)
|
299
|
-
|
300
|
-
if namespace_inheritable(:version)
|
301
|
-
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
302
|
-
versions: namespace_inheritable(:version)&.flatten,
|
303
|
-
version_options: namespace_inheritable(:version_options),
|
304
|
-
prefix: namespace_inheritable(:root_prefix),
|
305
|
-
mount_path: namespace_stackable(:mount_path).first
|
306
|
-
end
|
307
|
-
|
308
|
-
stack.use Grape::Middleware::Formatter,
|
309
|
-
format: namespace_inheritable(:format),
|
310
|
-
default_format: namespace_inheritable(:default_format) || :txt,
|
311
|
-
content_types: namespace_stackable_with_hash(:content_types),
|
312
|
-
formatters: namespace_stackable_with_hash(:formatters),
|
313
|
-
parsers: namespace_stackable_with_hash(:parsers)
|
314
|
-
|
315
|
-
builder = stack.build
|
316
|
-
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
317
|
-
builder.to_app
|
318
|
-
end
|
319
|
-
|
320
|
-
def build_helpers
|
321
|
-
helpers = namespace_stackable(:helpers)
|
322
|
-
Module.new { helpers&.each { |mod_to_include| include mod_to_include } }
|
323
|
-
end
|
324
|
-
|
325
|
-
private :build_stack, :build_helpers
|
326
|
-
|
327
286
|
def execute
|
328
287
|
@block&.call(self)
|
329
288
|
end
|
@@ -338,7 +297,7 @@ module Grape
|
|
338
297
|
@lazy_initialize_lock.synchronize do
|
339
298
|
return true if @lazy_initialized
|
340
299
|
|
341
|
-
@helpers = build_helpers
|
300
|
+
@helpers = build_helpers&.tap { |mod| self.class.include mod }
|
342
301
|
@app = options[:app] || build_stack(@helpers)
|
343
302
|
|
344
303
|
@lazy_initialized = true
|
@@ -371,45 +330,88 @@ module Grape
|
|
371
330
|
extend post_extension if post_extension
|
372
331
|
end
|
373
332
|
|
374
|
-
|
375
|
-
|
333
|
+
%i[befores before_validations after_validations afters finallies].each do |method|
|
334
|
+
define_method method do
|
335
|
+
namespace_stackable(method)
|
336
|
+
end
|
376
337
|
end
|
377
338
|
|
378
|
-
def
|
379
|
-
|
380
|
-
end
|
339
|
+
def validations
|
340
|
+
return enum_for(:validations) unless block_given?
|
381
341
|
|
382
|
-
|
383
|
-
|
342
|
+
route_setting(:saved_validations)&.each do |saved_validation|
|
343
|
+
yield Grape::Validations::ValidatorFactory.create_validator(saved_validation)
|
344
|
+
end
|
384
345
|
end
|
385
346
|
|
386
|
-
def
|
387
|
-
|
347
|
+
def options?
|
348
|
+
options[:options_route_enabled] &&
|
349
|
+
env[Rack::REQUEST_METHOD] == Rack::OPTIONS
|
388
350
|
end
|
389
351
|
|
390
|
-
|
391
|
-
namespace_stackable(:finallies)
|
392
|
-
end
|
352
|
+
private
|
393
353
|
|
394
|
-
def
|
395
|
-
|
354
|
+
def build_stack(helpers)
|
355
|
+
stack = Grape::Middleware::Stack.new
|
396
356
|
|
397
|
-
|
398
|
-
|
357
|
+
content_types = namespace_stackable_with_hash(:content_types)
|
358
|
+
format = namespace_inheritable(:format)
|
359
|
+
|
360
|
+
stack.use Rack::Head
|
361
|
+
stack.use Rack::Lint if lint?
|
362
|
+
stack.use Class.new(Grape::Middleware::Error),
|
363
|
+
helpers: helpers,
|
364
|
+
format: format,
|
365
|
+
content_types: content_types,
|
366
|
+
default_status: namespace_inheritable(:default_error_status),
|
367
|
+
rescue_all: namespace_inheritable(:rescue_all),
|
368
|
+
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
369
|
+
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
370
|
+
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
371
|
+
rescue_options: namespace_stackable_with_hash(:rescue_options),
|
372
|
+
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers),
|
373
|
+
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers),
|
374
|
+
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
|
375
|
+
grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
|
376
|
+
|
377
|
+
stack.concat namespace_stackable(:middleware)
|
378
|
+
|
379
|
+
if namespace_inheritable(:version).present?
|
380
|
+
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
381
|
+
versions: namespace_inheritable(:version).flatten,
|
382
|
+
version_options: namespace_inheritable(:version_options),
|
383
|
+
prefix: namespace_inheritable(:root_prefix),
|
384
|
+
mount_path: namespace_stackable(:mount_path).first
|
399
385
|
end
|
386
|
+
|
387
|
+
stack.use Grape::Middleware::Formatter,
|
388
|
+
format: format,
|
389
|
+
default_format: namespace_inheritable(:default_format) || :txt,
|
390
|
+
content_types: content_types,
|
391
|
+
formatters: namespace_stackable_with_hash(:formatters),
|
392
|
+
parsers: namespace_stackable_with_hash(:parsers)
|
393
|
+
|
394
|
+
builder = stack.build
|
395
|
+
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
396
|
+
builder.to_app
|
400
397
|
end
|
401
398
|
|
402
|
-
def
|
403
|
-
|
404
|
-
|
399
|
+
def build_helpers
|
400
|
+
helpers = namespace_stackable(:helpers)
|
401
|
+
return if helpers.empty?
|
402
|
+
|
403
|
+
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
405
404
|
end
|
406
405
|
|
407
|
-
def
|
408
|
-
|
406
|
+
def build_response_cookies
|
407
|
+
response_cookies do |name, value|
|
408
|
+
cookie_value = value.is_a?(Hash) ? value : { value: value }
|
409
|
+
Rack::Utils.set_cookie_header! header, name, cookie_value
|
410
|
+
end
|
409
411
|
end
|
410
412
|
|
411
|
-
def
|
412
|
-
|
413
|
+
def lint?
|
414
|
+
namespace_inheritable(:lint) || Grape.config.lint
|
413
415
|
end
|
414
416
|
end
|
415
417
|
end
|
@@ -11,11 +11,6 @@ module Grape
|
|
11
11
|
API_VENDOR = 'api.vendor'
|
12
12
|
API_FORMAT = 'api.format'
|
13
13
|
|
14
|
-
RACK_INPUT = 'rack.input'
|
15
|
-
RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
|
16
|
-
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
|
17
|
-
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
|
18
|
-
|
19
14
|
GRAPE_REQUEST = 'grape.request'
|
20
15
|
GRAPE_REQUEST_HEADERS = 'grape.request.headers'
|
21
16
|
GRAPE_REQUEST_PARAMS = 'grape.request.params'
|
@@ -2,36 +2,66 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
class Base
|
6
|
+
class << self
|
7
|
+
def call(message, backtrace, options = {}, env = nil, original_exception = nil)
|
8
|
+
merge_backtrace = backtrace.present? && options.dig(:rescue_options, :backtrace)
|
9
|
+
merge_original_exception = original_exception && options.dig(:rescue_options, :original_exception)
|
10
|
+
|
11
|
+
wrapped_message = wrap_message(present(message, env))
|
12
|
+
if wrapped_message.is_a?(Hash)
|
13
|
+
wrapped_message[:backtrace] = backtrace if merge_backtrace
|
14
|
+
wrapped_message[:original_exception] = original_exception.inspect if merge_original_exception
|
15
|
+
end
|
16
|
+
|
17
|
+
format_structured_message(wrapped_message)
|
12
18
|
end
|
13
19
|
|
14
|
-
|
20
|
+
def present(message, env)
|
21
|
+
present_options = {}
|
22
|
+
presented_message = message
|
23
|
+
if presented_message.is_a?(Hash)
|
24
|
+
presented_message = presented_message.dup
|
25
|
+
present_options[:with] = presented_message.delete(:with)
|
26
|
+
end
|
27
|
+
|
28
|
+
presenter = env[Grape::Env::API_ENDPOINT].entity_class_for_obj(presented_message, present_options)
|
29
|
+
|
30
|
+
unless presenter || env[Grape::Env::GRAPE_ROUTING_ARGS].nil?
|
31
|
+
# env['api.endpoint'].route does not work when the error occurs within a middleware
|
32
|
+
# the Endpoint does not have a valid env at this moment
|
33
|
+
http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
|
34
|
+
|
35
|
+
found_code = http_codes.find do |http_code|
|
36
|
+
(http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
|
37
|
+
end if env[Grape::Env::API_ENDPOINT].request
|
15
38
|
|
16
|
-
|
17
|
-
|
18
|
-
# the Endpoint does not have a valid env at this moment
|
19
|
-
http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
|
39
|
+
presenter = found_code[2] if found_code
|
40
|
+
end
|
20
41
|
|
21
|
-
|
22
|
-
|
23
|
-
|
42
|
+
if presenter
|
43
|
+
embeds = { env: env }
|
44
|
+
embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
|
45
|
+
presented_message = presenter.represent(presented_message, embeds).serializable_hash
|
46
|
+
end
|
24
47
|
|
25
|
-
|
48
|
+
presented_message
|
26
49
|
end
|
27
50
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
51
|
+
def wrap_message(message)
|
52
|
+
return message if message.is_a?(Hash)
|
53
|
+
|
54
|
+
{ message: message }
|
55
|
+
end
|
56
|
+
|
57
|
+
def format_structured_message(_structured_message)
|
58
|
+
raise NotImplementedError
|
32
59
|
end
|
33
60
|
|
34
|
-
|
61
|
+
def inherited(klass)
|
62
|
+
super
|
63
|
+
ErrorFormatter.register(klass)
|
64
|
+
end
|
35
65
|
end
|
36
66
|
end
|
37
67
|
end
|
@@ -2,27 +2,19 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
6
|
-
extend Base
|
7
|
-
|
5
|
+
class Json < Base
|
8
6
|
class << self
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
rescue_options = options[:rescue_options] || {}
|
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
|
15
|
-
::Grape::Json.dump(result)
|
7
|
+
def format_structured_message(structured_message)
|
8
|
+
::Grape::Json.dump(structured_message)
|
16
9
|
end
|
17
10
|
|
18
11
|
private
|
19
12
|
|
20
13
|
def wrap_message(message)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
14
|
+
return message if message.is_a?(Hash)
|
15
|
+
return message.as_json if message.is_a?(Exceptions::ValidationErrors)
|
16
|
+
|
17
|
+
{ error: ensure_utf8(message) }
|
26
18
|
end
|
27
19
|
|
28
20
|
def ensure_utf8(message)
|
@@ -2,25 +2,19 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
result = message.is_a?(Hash) ? ::Grape::Json.dump(message) : message
|
13
|
-
rescue_options = options[:rescue_options] || {}
|
14
|
-
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
15
|
-
result += "\r\n backtrace:"
|
16
|
-
result += backtrace.join("\r\n ")
|
5
|
+
class Txt < Base
|
6
|
+
def self.format_structured_message(structured_message)
|
7
|
+
message = structured_message[:message] || Grape::Json.dump(structured_message)
|
8
|
+
Array.wrap(message).tap do |final_message|
|
9
|
+
if structured_message.key?(:backtrace)
|
10
|
+
final_message << 'backtrace:'
|
11
|
+
final_message.concat(structured_message[:backtrace])
|
17
12
|
end
|
18
|
-
if
|
19
|
-
|
20
|
-
|
13
|
+
if structured_message.key?(:original_exception)
|
14
|
+
final_message << 'original exception:'
|
15
|
+
final_message << structured_message[:original_exception]
|
21
16
|
end
|
22
|
-
|
23
|
-
end
|
17
|
+
end.join("\r\n ")
|
24
18
|
end
|
25
19
|
end
|
26
20
|
end
|
@@ -2,19 +2,9 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
class << self
|
9
|
-
def call(message, backtrace, options = {}, env = nil, original_exception = nil)
|
10
|
-
message = present(message, env)
|
11
|
-
|
12
|
-
result = message.is_a?(Hash) ? message : { message: message }
|
13
|
-
rescue_options = options[:rescue_options] || {}
|
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
|
16
|
-
result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
|
17
|
-
end
|
5
|
+
class Xml < Base
|
6
|
+
def self.format_structured_message(structured_message)
|
7
|
+
structured_message.respond_to?(:to_xml) ? structured_message.to_xml(root: :error) : structured_message.to_s
|
18
8
|
end
|
19
9
|
end
|
20
10
|
end
|
@@ -2,34 +2,14 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
extend Util::
|
5
|
+
extend Grape::Util::Registry
|
6
6
|
|
7
|
-
|
8
|
-
def builtin_formatters
|
9
|
-
@builtin_formatters ||= {
|
10
|
-
serializable_hash: Grape::ErrorFormatter::Json,
|
11
|
-
json: Grape::ErrorFormatter::Json,
|
12
|
-
jsonapi: Grape::ErrorFormatter::Json,
|
13
|
-
txt: Grape::ErrorFormatter::Txt,
|
14
|
-
xml: Grape::ErrorFormatter::Xml
|
15
|
-
}
|
16
|
-
end
|
7
|
+
module_function
|
17
8
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
9
|
+
def formatter_for(format, error_formatters = nil, default_error_formatter = nil)
|
10
|
+
return error_formatters[format] if error_formatters&.key?(format)
|
21
11
|
|
22
|
-
|
23
|
-
spec = formatters(**options)[api_format]
|
24
|
-
case spec
|
25
|
-
when nil
|
26
|
-
options[:default_error_formatter] || Grape::ErrorFormatter::Txt
|
27
|
-
when Symbol
|
28
|
-
method(spec)
|
29
|
-
else
|
30
|
-
spec
|
31
|
-
end
|
32
|
-
end
|
12
|
+
registry[format] || default_error_formatter || Grape::ErrorFormatter::Txt
|
33
13
|
end
|
34
14
|
end
|
35
15
|
end
|