grape 2.2.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 +55 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +41 -18
- data/UPGRADING.md +75 -1
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +25 -60
- data/lib/grape/api.rb +44 -76
- data/lib/grape/cookies.rb +31 -25
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/desc.rb +27 -24
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/helpers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +17 -40
- data/lib/grape/dsl/parameters.rb +5 -5
- data/lib/grape/dsl/routing.rb +14 -13
- data/lib/grape/endpoint.rb +100 -106
- data/lib/grape/error_formatter/base.rb +51 -21
- data/lib/grape/error_formatter/json.rb +7 -24
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +13 -20
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +4 -12
- 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 -4
- data/lib/grape/exceptions/validation_errors.rb +2 -2
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
- data/lib/grape/extensions/hash.rb +2 -1
- 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 +4 -12
- data/lib/grape/locale/en.yml +44 -44
- data/lib/grape/middleware/auth/base.rb +11 -32
- data/lib/grape/middleware/auth/dsl.rb +23 -29
- data/lib/grape/middleware/base.rb +30 -11
- data/lib/grape/middleware/error.rb +18 -24
- data/lib/grape/middleware/formatter.rb +39 -73
- data/lib/grape/middleware/stack.rb +26 -36
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -3
- data/lib/grape/middleware/versioner/base.rb +74 -0
- data/lib/grape/middleware/versioner/header.rb +4 -10
- data/lib/grape/middleware/versioner/param.rb +2 -5
- data/lib/grape/middleware/versioner/path.rb +0 -2
- data/lib/grape/middleware/versioner.rb +5 -3
- data/lib/grape/namespace.rb +1 -1
- 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 -7
- data/lib/grape/path.rb +39 -56
- data/lib/grape/request.rb +162 -23
- data/lib/grape/router/base_route.rb +2 -2
- data/lib/grape/router/greedy_route.rb +2 -2
- data/lib/grape/router/pattern.rb +23 -18
- data/lib/grape/router/route.rb +14 -6
- data/lib/grape/router.rb +30 -12
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/validations/contract_scope.rb +2 -39
- data/lib/grape/validations/params_scope.rb +15 -14
- data/lib/grape/validations/types/dry_type_coercer.rb +10 -6
- 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 +7 -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 +1 -1
- data/lib/grape/validations/validators/except_values_validator.rb +2 -2
- data/lib/grape/validations/validators/length_validator.rb +1 -1
- 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 +15 -57
- data/lib/grape/validations.rb +8 -17
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +14 -2
- metadata +24 -16
- data/lib/grape/http/headers.rb +0 -55
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/middleware/versioner_helpers.rb +0 -75
- data/lib/grape/util/lazy/object.rb +0 -45
- data/lib/grape/validations/types/build_coercer.rb +0 -92
data/lib/grape/endpoint.rb
CHANGED
@@ -6,11 +6,15 @@ 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
20
|
def new(...)
|
@@ -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
|
@@ -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
|
@@ -196,19 +200,14 @@ module Grape
|
|
196
200
|
version.length == 1 ? version.first : version
|
197
201
|
end
|
198
202
|
|
199
|
-
def merge_route_options(**default)
|
200
|
-
options[:route_options].clone.merge!(**default)
|
201
|
-
end
|
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
|
207
|
+
def prepare_default_path_settings
|
208
208
|
namespace_stackable_hash = inheritable_setting.namespace_stackable.to_hash
|
209
209
|
namespace_inheritable_hash = inheritable_setting.namespace_inheritable.to_hash
|
210
|
-
|
211
|
-
Path.new(path, namespace, path_settings)
|
210
|
+
namespace_stackable_hash.merge!(namespace_inheritable_hash)
|
212
211
|
end
|
213
212
|
|
214
213
|
def namespace
|
@@ -229,7 +228,7 @@ module Grape
|
|
229
228
|
# Return the collection of endpoints within this endpoint.
|
230
229
|
# This is the case when an Grape::API mounts another Grape::API.
|
231
230
|
def endpoints
|
232
|
-
|
231
|
+
@endpoints ||= options[:app].try(:endpoints)
|
233
232
|
end
|
234
233
|
|
235
234
|
def equals?(endpoint)
|
@@ -249,19 +248,16 @@ module Grape
|
|
249
248
|
|
250
249
|
def run
|
251
250
|
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
|
252
|
-
@header = Grape::Util::Header.new
|
253
251
|
@request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with))
|
254
|
-
@params = @request.params
|
255
|
-
@headers = @request.headers
|
256
252
|
begin
|
257
|
-
cookies.read(@request)
|
258
253
|
self.class.run_before_each(self)
|
259
254
|
run_filters befores, :before
|
260
255
|
|
261
|
-
if (
|
262
|
-
|
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?
|
263
259
|
|
264
|
-
header
|
260
|
+
header 'Allow', header['Allow']
|
265
261
|
response_object = ''
|
266
262
|
status 204
|
267
263
|
else
|
@@ -272,7 +268,7 @@ module Grape
|
|
272
268
|
end
|
273
269
|
|
274
270
|
run_filters afters, :after
|
275
|
-
|
271
|
+
build_response_cookies
|
276
272
|
|
277
273
|
# status verifies body presence when DELETE
|
278
274
|
@body ||= response_object
|
@@ -287,59 +283,6 @@ module Grape
|
|
287
283
|
end
|
288
284
|
end
|
289
285
|
|
290
|
-
def build_stack(helpers)
|
291
|
-
stack = Grape::Middleware::Stack.new
|
292
|
-
|
293
|
-
content_types = namespace_stackable_with_hash(:content_types)
|
294
|
-
format = namespace_inheritable(:format)
|
295
|
-
|
296
|
-
stack.use Rack::Head
|
297
|
-
stack.use Class.new(Grape::Middleware::Error),
|
298
|
-
helpers: helpers,
|
299
|
-
format: format,
|
300
|
-
content_types: content_types,
|
301
|
-
default_status: namespace_inheritable(:default_error_status),
|
302
|
-
rescue_all: namespace_inheritable(:rescue_all),
|
303
|
-
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
304
|
-
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
305
|
-
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
306
|
-
rescue_options: namespace_stackable_with_hash(:rescue_options),
|
307
|
-
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers),
|
308
|
-
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers),
|
309
|
-
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
|
310
|
-
grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
|
311
|
-
|
312
|
-
stack.concat namespace_stackable(:middleware)
|
313
|
-
|
314
|
-
if namespace_inheritable(:version).present?
|
315
|
-
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
316
|
-
versions: namespace_inheritable(:version).flatten,
|
317
|
-
version_options: namespace_inheritable(:version_options),
|
318
|
-
prefix: namespace_inheritable(:root_prefix),
|
319
|
-
mount_path: namespace_stackable(:mount_path).first
|
320
|
-
end
|
321
|
-
|
322
|
-
stack.use Grape::Middleware::Formatter,
|
323
|
-
format: format,
|
324
|
-
default_format: namespace_inheritable(:default_format) || :txt,
|
325
|
-
content_types: content_types,
|
326
|
-
formatters: namespace_stackable_with_hash(:formatters),
|
327
|
-
parsers: namespace_stackable_with_hash(:parsers)
|
328
|
-
|
329
|
-
builder = stack.build
|
330
|
-
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
331
|
-
builder.to_app
|
332
|
-
end
|
333
|
-
|
334
|
-
def build_helpers
|
335
|
-
helpers = namespace_stackable(:helpers)
|
336
|
-
return if helpers.empty?
|
337
|
-
|
338
|
-
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
339
|
-
end
|
340
|
-
|
341
|
-
private :build_stack, :build_helpers
|
342
|
-
|
343
286
|
def execute
|
344
287
|
@block&.call(self)
|
345
288
|
end
|
@@ -387,37 +330,88 @@ module Grape
|
|
387
330
|
extend post_extension if post_extension
|
388
331
|
end
|
389
332
|
|
390
|
-
|
391
|
-
|
333
|
+
%i[befores before_validations after_validations afters finallies].each do |method|
|
334
|
+
define_method method do
|
335
|
+
namespace_stackable(method)
|
336
|
+
end
|
392
337
|
end
|
393
338
|
|
394
|
-
def
|
395
|
-
|
396
|
-
end
|
339
|
+
def validations
|
340
|
+
return enum_for(:validations) unless block_given?
|
397
341
|
|
398
|
-
|
399
|
-
|
342
|
+
route_setting(:saved_validations)&.each do |saved_validation|
|
343
|
+
yield Grape::Validations::ValidatorFactory.create_validator(saved_validation)
|
344
|
+
end
|
400
345
|
end
|
401
346
|
|
402
|
-
def
|
403
|
-
|
347
|
+
def options?
|
348
|
+
options[:options_route_enabled] &&
|
349
|
+
env[Rack::REQUEST_METHOD] == Rack::OPTIONS
|
404
350
|
end
|
405
351
|
|
406
|
-
|
407
|
-
|
352
|
+
private
|
353
|
+
|
354
|
+
def build_stack(helpers)
|
355
|
+
stack = Grape::Middleware::Stack.new
|
356
|
+
|
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
|
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
|
408
397
|
end
|
409
398
|
|
410
|
-
def
|
411
|
-
|
399
|
+
def build_helpers
|
400
|
+
helpers = namespace_stackable(:helpers)
|
401
|
+
return if helpers.empty?
|
412
402
|
|
413
|
-
|
414
|
-
|
403
|
+
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
404
|
+
end
|
405
|
+
|
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
|
415
410
|
end
|
416
411
|
end
|
417
412
|
|
418
|
-
def
|
419
|
-
|
420
|
-
env[Rack::REQUEST_METHOD] == Rack::OPTIONS
|
413
|
+
def lint?
|
414
|
+
namespace_inheritable(:lint) || Grape.config.lint
|
421
415
|
end
|
422
416
|
end
|
423
417
|
end
|
@@ -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,28 +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
|
-
result = merge_rescue_options(result, backtrace, options, original_exception) if result.is_a?(Hash)
|
13
|
-
|
14
|
-
::Grape::Json.dump(result)
|
7
|
+
def format_structured_message(structured_message)
|
8
|
+
::Grape::Json.dump(structured_message)
|
15
9
|
end
|
16
10
|
|
17
11
|
private
|
18
12
|
|
19
13
|
def wrap_message(message)
|
20
|
-
if message.is_a?(Hash)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
else
|
25
|
-
{ error: ensure_utf8(message) }
|
26
|
-
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) }
|
27
18
|
end
|
28
19
|
|
29
20
|
def ensure_utf8(message)
|
@@ -31,14 +22,6 @@ module Grape
|
|
31
22
|
|
32
23
|
message.encode('UTF-8', invalid: :replace, undef: :replace)
|
33
24
|
end
|
34
|
-
|
35
|
-
def merge_rescue_options(result, backtrace, options, original_exception)
|
36
|
-
rescue_options = options[:rescue_options] || {}
|
37
|
-
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
38
|
-
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
39
|
-
|
40
|
-
result
|
41
|
-
end
|
42
25
|
end
|
43
26
|
end
|
44
27
|
end
|
@@ -2,26 +2,19 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
if rescue_options[:original_exception] && original_exception
|
20
|
-
final_result << 'original exception:'
|
21
|
-
final_result << original_exception.inspect
|
22
|
-
end
|
23
|
-
end.join("\r\n ")
|
24
|
-
end
|
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])
|
12
|
+
end
|
13
|
+
if structured_message.key?(:original_exception)
|
14
|
+
final_message << 'original exception:'
|
15
|
+
final_message << structured_message[:original_exception]
|
16
|
+
end
|
17
|
+
end.join("\r\n ")
|
25
18
|
end
|
26
19
|
end
|
27
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,22 +2,14 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
5
|
+
extend Grape::Util::Registry
|
6
6
|
|
7
|
-
|
8
|
-
serializable_hash: Grape::ErrorFormatter::Json,
|
9
|
-
json: Grape::ErrorFormatter::Json,
|
10
|
-
jsonapi: Grape::ErrorFormatter::Json,
|
11
|
-
txt: Grape::ErrorFormatter::Txt,
|
12
|
-
xml: Grape::ErrorFormatter::Xml
|
13
|
-
}.freeze
|
7
|
+
module_function
|
14
8
|
|
15
9
|
def formatter_for(format, error_formatters = nil, default_error_formatter = nil)
|
16
|
-
|
17
|
-
end
|
10
|
+
return error_formatters[format] if error_formatters&.key?(format)
|
18
11
|
|
19
|
-
|
20
|
-
error_formatters&.key?(format) ? error_formatters[format] : DEFAULTS[format]
|
12
|
+
registry[format] || default_error_formatter || Grape::ErrorFormatter::Txt
|
21
13
|
end
|
22
14
|
end
|
23
15
|
end
|
@@ -9,7 +9,7 @@ module Grape
|
|
9
9
|
|
10
10
|
attr_reader :status, :headers
|
11
11
|
|
12
|
-
def initialize(status: nil, message: nil, headers: nil
|
12
|
+
def initialize(status: nil, message: nil, headers: nil)
|
13
13
|
super(message)
|
14
14
|
|
15
15
|
@status = status
|
@@ -26,42 +26,32 @@ module Grape
|
|
26
26
|
# if BASE_ATTRIBUTES_KEY.key respond to a string message, then short_message is returned
|
27
27
|
# if BASE_ATTRIBUTES_KEY.key respond to a Hash, means it may have problem , summary and resolution
|
28
28
|
def compose_message(key, **attributes)
|
29
|
-
short_message = translate_message(key,
|
30
|
-
|
31
|
-
@problem = problem(key, **attributes)
|
32
|
-
@summary = summary(key, **attributes)
|
33
|
-
@resolution = resolution(key, **attributes)
|
34
|
-
[['Problem', @problem], ['Summary', @summary], ['Resolution', @resolution]].each_with_object(+'') do |detail_array, message|
|
35
|
-
message << "\n#{detail_array[0]}:\n #{detail_array[1]}" unless detail_array[1].blank?
|
36
|
-
message
|
37
|
-
end
|
38
|
-
else
|
39
|
-
short_message
|
40
|
-
end
|
41
|
-
end
|
29
|
+
short_message = translate_message(key, attributes)
|
30
|
+
return short_message unless short_message.is_a?(Hash)
|
42
31
|
|
43
|
-
|
44
|
-
|
32
|
+
each_steps(key, attributes).with_object(+'') do |detail_array, message|
|
33
|
+
message << "\n#{detail_array[0]}:\n #{detail_array[1]}" unless detail_array[1].blank?
|
34
|
+
end
|
45
35
|
end
|
46
36
|
|
47
|
-
def
|
48
|
-
|
49
|
-
end
|
37
|
+
def each_steps(key, attributes)
|
38
|
+
return enum_for(:each_steps, key, attributes) unless block_given?
|
50
39
|
|
51
|
-
|
52
|
-
translate_message(:"#{key}.
|
40
|
+
yield 'Problem', translate_message(:"#{key}.problem", attributes)
|
41
|
+
yield 'Summary', translate_message(:"#{key}.summary", attributes)
|
42
|
+
yield 'Resolution', translate_message(:"#{key}.resolution", attributes)
|
53
43
|
end
|
54
44
|
|
55
|
-
def translate_attributes(keys,
|
45
|
+
def translate_attributes(keys, options = {})
|
56
46
|
keys.map do |key|
|
57
|
-
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", default: key
|
47
|
+
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", options.merge(default: key.to_s))
|
58
48
|
end.join(', ')
|
59
49
|
end
|
60
50
|
|
61
|
-
def translate_message(key,
|
51
|
+
def translate_message(key, options = {})
|
62
52
|
case key
|
63
53
|
when Symbol
|
64
|
-
translate("#{BASE_MESSAGES_KEY}.#{key}", default: ''
|
54
|
+
translate("#{BASE_MESSAGES_KEY}.#{key}", options.merge(default: ''))
|
65
55
|
when Proc
|
66
56
|
key.call
|
67
57
|
else
|
@@ -69,14 +59,12 @@ module Grape
|
|
69
59
|
end
|
70
60
|
end
|
71
61
|
|
72
|
-
def translate(key,
|
73
|
-
options = options.dup
|
74
|
-
options[:default] &&= options[:default].to_s
|
62
|
+
def translate(key, options)
|
75
63
|
message = ::I18n.translate(key, **options)
|
76
|
-
message.presence || fallback_message(key,
|
64
|
+
message.presence || fallback_message(key, options)
|
77
65
|
end
|
78
66
|
|
79
|
-
def fallback_message(key,
|
67
|
+
def fallback_message(key, options)
|
80
68
|
if ::I18n.enforce_available_locales && ::I18n.available_locales.exclude?(FALLBACK_LOCALE)
|
81
69
|
key
|
82
70
|
else
|