grape 2.1.3 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +45 -0
- data/README.md +9 -7
- data/UPGRADING.md +27 -0
- data/grape.gemspec +7 -6
- data/lib/grape/api/instance.rb +22 -58
- data/lib/grape/api.rb +2 -11
- data/lib/grape/content_types.rb +13 -8
- data/lib/grape/dsl/desc.rb +27 -24
- data/lib/grape/dsl/helpers.rb +7 -3
- data/lib/grape/dsl/inside_route.rb +18 -24
- data/lib/grape/dsl/parameters.rb +2 -2
- data/lib/grape/dsl/request_response.rb +14 -18
- data/lib/grape/dsl/routing.rb +5 -12
- data/lib/grape/endpoint.rb +90 -82
- data/lib/grape/error_formatter/base.rb +51 -21
- data/lib/grape/error_formatter/json.rb +7 -15
- data/lib/grape/error_formatter/jsonapi.rb +7 -0
- 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 +5 -25
- data/lib/grape/exceptions/base.rb +18 -30
- data/lib/grape/exceptions/validation.rb +5 -4
- data/lib/grape/exceptions/validation_errors.rb +2 -2
- 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/http/headers.rb +1 -0
- data/lib/grape/locale/en.yml +1 -0
- data/lib/grape/middleware/base.rb +14 -13
- data/lib/grape/middleware/error.rb +13 -9
- data/lib/grape/middleware/formatter.rb +3 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +7 -30
- data/lib/grape/middleware/versioner/base.rb +82 -0
- data/lib/grape/middleware/versioner/header.rb +89 -10
- data/lib/grape/middleware/versioner/param.rb +4 -22
- data/lib/grape/middleware/versioner/path.rb +10 -32
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/jsonapi.rb +7 -0
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -23
- data/lib/grape/path.rb +39 -56
- data/lib/grape/request.rb +2 -2
- 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 +13 -5
- data/lib/grape/router.rb +5 -5
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/validations/contract_scope.rb +2 -39
- data/lib/grape/validations/params_scope.rb +7 -11
- 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 +5 -9
- 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 +1 -1
- data/lib/grape/validations/validators/length_validator.rb +11 -4
- data/lib/grape/validations/validators/regexp_validator.rb +1 -1
- 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 +1 -1
- metadata +15 -12
- data/lib/grape/util/accept_header_handler.rb +0 -105
- data/lib/grape/util/registrable.rb +0 -15
- data/lib/grape/validations/types/build_coercer.rb +0 -92
data/lib/grape/endpoint.rb
CHANGED
@@ -114,10 +114,10 @@ module Grape
|
|
114
114
|
# Update our settings from a given set of stackable parameters. Used when
|
115
115
|
# the endpoint's API is mounted under another one.
|
116
116
|
def inherit_settings(namespace_stackable)
|
117
|
-
|
117
|
+
parent_validations = namespace_stackable[:validations]
|
118
|
+
inheritable_setting.route[:saved_validations].concat(parent_validations) if parent_validations.any?
|
118
119
|
parent_declared_params = namespace_stackable[:declared_params]
|
119
|
-
|
120
|
-
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
|
120
|
+
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params.any?
|
121
121
|
|
122
122
|
endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
|
123
123
|
end
|
@@ -145,27 +145,28 @@ module Grape
|
|
145
145
|
end
|
146
146
|
|
147
147
|
def mount_in(router)
|
148
|
-
if endpoints
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
end
|
148
|
+
return endpoints.each { |e| e.mount_in(router) } if endpoints
|
149
|
+
|
150
|
+
reset_routes!
|
151
|
+
routes.each do |route|
|
152
|
+
router.append(route.apply(self))
|
153
|
+
next unless !namespace_inheritable(:do_not_route_head) && route.request_method == Rack::GET
|
154
|
+
|
155
|
+
route.dup.then do |head_route|
|
156
|
+
head_route.convert_to_head_request!
|
157
|
+
router.append(head_route.apply(self))
|
159
158
|
end
|
160
159
|
end
|
161
160
|
end
|
162
161
|
|
163
162
|
def to_routes
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
163
|
+
default_route_options = prepare_default_route_attributes
|
164
|
+
default_path_settings = prepare_default_path_settings
|
165
|
+
|
166
|
+
map_routes do |method, raw_path|
|
167
|
+
prepared_path = Path.new(raw_path, namespace, default_path_settings)
|
168
|
+
params = options[:route_options].present? ? options[:route_options].merge(default_route_options) : default_route_options
|
169
|
+
route = Grape::Router::Route.new(method, prepared_path.origin, prepared_path.suffix, params)
|
169
170
|
route.apply(self)
|
170
171
|
end.flatten
|
171
172
|
end
|
@@ -191,23 +192,19 @@ module Grape
|
|
191
192
|
|
192
193
|
def prepare_version
|
193
194
|
version = namespace_inheritable(:version)
|
194
|
-
return
|
195
|
-
return if version.empty?
|
195
|
+
return if version.blank?
|
196
196
|
|
197
197
|
version.length == 1 ? version.first : version
|
198
198
|
end
|
199
199
|
|
200
|
-
def merge_route_options(**default)
|
201
|
-
options[:route_options].clone.merge!(**default)
|
202
|
-
end
|
203
|
-
|
204
200
|
def map_routes
|
205
201
|
options[:method].map { |method| options[:path].map { |path| yield method, path } }
|
206
202
|
end
|
207
203
|
|
208
|
-
def
|
209
|
-
|
210
|
-
|
204
|
+
def prepare_default_path_settings
|
205
|
+
namespace_stackable_hash = inheritable_setting.namespace_stackable.to_hash
|
206
|
+
namespace_inheritable_hash = inheritable_setting.namespace_inheritable.to_hash
|
207
|
+
namespace_stackable_hash.merge!(namespace_inheritable_hash)
|
211
208
|
end
|
212
209
|
|
213
210
|
def namespace
|
@@ -235,6 +232,15 @@ module Grape
|
|
235
232
|
(options == endpoint.options) && (inheritable_setting.to_hash == endpoint.inheritable_setting.to_hash)
|
236
233
|
end
|
237
234
|
|
235
|
+
# The purpose of this override is solely for stripping internals when an error occurs while calling
|
236
|
+
# an endpoint through an api. See https://github.com/ruby-grape/grape/issues/2398
|
237
|
+
# Otherwise, it calls super.
|
238
|
+
def inspect
|
239
|
+
return super unless env
|
240
|
+
|
241
|
+
"#{self.class} in '#{route.origin}' endpoint"
|
242
|
+
end
|
243
|
+
|
238
244
|
protected
|
239
245
|
|
240
246
|
def run
|
@@ -249,9 +255,10 @@ module Grape
|
|
249
255
|
run_filters befores, :before
|
250
256
|
|
251
257
|
if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
|
252
|
-
|
258
|
+
allow_header_value = allowed_methods.join(', ')
|
259
|
+
raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allow_header_value)) unless options?
|
253
260
|
|
254
|
-
header Grape::Http::Headers::ALLOW,
|
261
|
+
header Grape::Http::Headers::ALLOW, allow_header_value
|
255
262
|
response_object = ''
|
256
263
|
status 204
|
257
264
|
else
|
@@ -277,54 +284,6 @@ module Grape
|
|
277
284
|
end
|
278
285
|
end
|
279
286
|
|
280
|
-
def build_stack(helpers)
|
281
|
-
stack = Grape::Middleware::Stack.new
|
282
|
-
|
283
|
-
stack.use Rack::Head
|
284
|
-
stack.use Class.new(Grape::Middleware::Error),
|
285
|
-
helpers: helpers,
|
286
|
-
format: namespace_inheritable(:format),
|
287
|
-
content_types: namespace_stackable_with_hash(:content_types),
|
288
|
-
default_status: namespace_inheritable(:default_error_status),
|
289
|
-
rescue_all: namespace_inheritable(:rescue_all),
|
290
|
-
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
291
|
-
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
292
|
-
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
293
|
-
rescue_options: namespace_stackable_with_hash(:rescue_options) || {},
|
294
|
-
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers) || {},
|
295
|
-
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {},
|
296
|
-
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
|
297
|
-
grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
|
298
|
-
|
299
|
-
stack.concat namespace_stackable(:middleware)
|
300
|
-
|
301
|
-
if namespace_inheritable(:version)
|
302
|
-
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
303
|
-
versions: namespace_inheritable(:version)&.flatten,
|
304
|
-
version_options: namespace_inheritable(:version_options),
|
305
|
-
prefix: namespace_inheritable(:root_prefix),
|
306
|
-
mount_path: namespace_stackable(:mount_path).first
|
307
|
-
end
|
308
|
-
|
309
|
-
stack.use Grape::Middleware::Formatter,
|
310
|
-
format: namespace_inheritable(:format),
|
311
|
-
default_format: namespace_inheritable(:default_format) || :txt,
|
312
|
-
content_types: namespace_stackable_with_hash(:content_types),
|
313
|
-
formatters: namespace_stackable_with_hash(:formatters),
|
314
|
-
parsers: namespace_stackable_with_hash(:parsers)
|
315
|
-
|
316
|
-
builder = stack.build
|
317
|
-
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
318
|
-
builder.to_app
|
319
|
-
end
|
320
|
-
|
321
|
-
def build_helpers
|
322
|
-
helpers = namespace_stackable(:helpers)
|
323
|
-
Module.new { helpers&.each { |mod_to_include| include mod_to_include } }
|
324
|
-
end
|
325
|
-
|
326
|
-
private :build_stack, :build_helpers
|
327
|
-
|
328
287
|
def execute
|
329
288
|
@block&.call(self)
|
330
289
|
end
|
@@ -339,7 +298,7 @@ module Grape
|
|
339
298
|
@lazy_initialize_lock.synchronize do
|
340
299
|
return true if @lazy_initialized
|
341
300
|
|
342
|
-
@helpers = build_helpers
|
301
|
+
@helpers = build_helpers&.tap { |mod| self.class.include mod }
|
343
302
|
@app = options[:app] || build_stack(@helpers)
|
344
303
|
|
345
304
|
@lazy_initialized = true
|
@@ -396,7 +355,7 @@ module Grape
|
|
396
355
|
return enum_for(:validations) unless block_given?
|
397
356
|
|
398
357
|
route_setting(:saved_validations)&.each do |saved_validation|
|
399
|
-
yield Grape::Validations::ValidatorFactory.create_validator(
|
358
|
+
yield Grape::Validations::ValidatorFactory.create_validator(saved_validation)
|
400
359
|
end
|
401
360
|
end
|
402
361
|
|
@@ -405,8 +364,57 @@ module Grape
|
|
405
364
|
env[Rack::REQUEST_METHOD] == Rack::OPTIONS
|
406
365
|
end
|
407
366
|
|
408
|
-
|
409
|
-
|
367
|
+
private
|
368
|
+
|
369
|
+
def build_stack(helpers)
|
370
|
+
stack = Grape::Middleware::Stack.new
|
371
|
+
|
372
|
+
content_types = namespace_stackable_with_hash(:content_types)
|
373
|
+
format = namespace_inheritable(:format)
|
374
|
+
|
375
|
+
stack.use Rack::Head
|
376
|
+
stack.use Class.new(Grape::Middleware::Error),
|
377
|
+
helpers: helpers,
|
378
|
+
format: format,
|
379
|
+
content_types: content_types,
|
380
|
+
default_status: namespace_inheritable(:default_error_status),
|
381
|
+
rescue_all: namespace_inheritable(:rescue_all),
|
382
|
+
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
383
|
+
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
384
|
+
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
385
|
+
rescue_options: namespace_stackable_with_hash(:rescue_options),
|
386
|
+
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers),
|
387
|
+
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers),
|
388
|
+
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
|
389
|
+
grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
|
390
|
+
|
391
|
+
stack.concat namespace_stackable(:middleware)
|
392
|
+
|
393
|
+
if namespace_inheritable(:version).present?
|
394
|
+
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
395
|
+
versions: namespace_inheritable(:version).flatten,
|
396
|
+
version_options: namespace_inheritable(:version_options),
|
397
|
+
prefix: namespace_inheritable(:root_prefix),
|
398
|
+
mount_path: namespace_stackable(:mount_path).first
|
399
|
+
end
|
400
|
+
|
401
|
+
stack.use Grape::Middleware::Formatter,
|
402
|
+
format: format,
|
403
|
+
default_format: namespace_inheritable(:default_format) || :txt,
|
404
|
+
content_types: content_types,
|
405
|
+
formatters: namespace_stackable_with_hash(:formatters),
|
406
|
+
parsers: namespace_stackable_with_hash(:parsers)
|
407
|
+
|
408
|
+
builder = stack.build
|
409
|
+
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
410
|
+
builder.to_app
|
411
|
+
end
|
412
|
+
|
413
|
+
def build_helpers
|
414
|
+
helpers = namespace_stackable(:helpers)
|
415
|
+
return if helpers.empty?
|
416
|
+
|
417
|
+
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
410
418
|
end
|
411
419
|
end
|
412
420
|
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,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,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,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
|
@@ -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
|
@@ -2,16 +2,17 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Exceptions
|
5
|
-
class Validation <
|
5
|
+
class Validation < Base
|
6
6
|
attr_accessor :params, :message_key
|
7
7
|
|
8
|
-
def initialize(params:, message: nil,
|
8
|
+
def initialize(params:, message: nil, status: nil, headers: nil)
|
9
9
|
@params = params
|
10
10
|
if message
|
11
11
|
@message_key = message if message.is_a?(Symbol)
|
12
|
-
|
12
|
+
message = translate_message(message)
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
|
+
super(status: status, message: message, headers: headers)
|
15
16
|
end
|
16
17
|
|
17
18
|
# Remove all the unnecessary stuff from Grape::Exceptions::Base like status
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Exceptions
|
5
|
-
class ValidationErrors <
|
5
|
+
class ValidationErrors < Base
|
6
6
|
ERRORS_FORMAT_KEY = 'grape.errors.format'
|
7
7
|
DEFAULT_ERRORS_FORMAT = '%<attributes>s %<message>s'
|
8
8
|
|
@@ -10,7 +10,7 @@ module Grape
|
|
10
10
|
|
11
11
|
attr_reader :errors
|
12
12
|
|
13
|
-
def initialize(errors: [], headers: {}
|
13
|
+
def initialize(errors: [], headers: {})
|
14
14
|
@errors = errors.group_by(&:params)
|
15
15
|
super(message: full_messages.join(', '), status: 400, headers: headers)
|
16
16
|
end
|
data/lib/grape/formatter/json.rb
CHANGED
@@ -2,13 +2,11 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Formatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
return object.to_json if object.respond_to?(:to_json)
|
5
|
+
class Json < Base
|
6
|
+
def self.call(object, _env)
|
7
|
+
return object.to_json if object.respond_to?(:to_json)
|
9
8
|
|
10
|
-
|
11
|
-
end
|
9
|
+
::Grape::Json.dump(object)
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|