From f2015a30339119d8e8dea5be567b45cc61001f75 Mon Sep 17 00:00:00 2001 From: Michael Scrivo Date: Tue, 27 Jun 2023 18:39:33 -0400 Subject: [PATCH] Allow specifying a handler for grape_exceptions This allows you to customize the format of the error response for grape exceptions: For example, you could do something like this: ```rb rescue_from :grape_exceptions do |e| error!({ errors: [{ code: 'Error', message: e.message.squish }] }, e.status) end ``` which would render like this: ``` { "errors": [ { "code": "Error", "message": "Problem: message body does not match declared format Resolution: when specifying application/json as content-type, you must pass valid application/json in the request's 'body'" } ] } ``` --- lib/grape/dsl/request_response.rb | 1 + lib/grape/endpoint.rb | 3 ++- lib/grape/middleware/error.rb | 2 +- spec/grape/dsl/request_response_spec.rb | 24 ++++++++++++++++++++++-- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/grape/dsl/request_response.rb b/lib/grape/dsl/request_response.rb index 6129574fd4..2c4c8fad3a 100644 --- a/lib/grape/dsl/request_response.rb +++ b/lib/grape/dsl/request_response.rb @@ -116,6 +116,7 @@ def rescue_from(*args, &block) elsif args.include?(:grape_exceptions) namespace_inheritable(:rescue_all, true) namespace_inheritable(:rescue_grape_exceptions, true) + namespace_inheritable :grape_exceptions_rescue_handler, handler else handler_type = case options[:rescue_subclasses] diff --git a/lib/grape/endpoint.rb b/lib/grape/endpoint.rb index 9ddb91b78b..8dfe915bce 100644 --- a/lib/grape/endpoint.rb +++ b/lib/grape/endpoint.rb @@ -292,7 +292,8 @@ def build_stack(helpers) rescue_options: namespace_stackable_with_hash(:rescue_options) || {}, rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers) || {}, base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {}, - all_rescue_handler: namespace_inheritable(:all_rescue_handler) + all_rescue_handler: namespace_inheritable(:all_rescue_handler), + grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler) stack.concat namespace_stackable(:middleware) diff --git a/lib/grape/middleware/error.rb b/lib/grape/middleware/error.rb index 3e9d8c768e..50e453d52e 100644 --- a/lib/grape/middleware/error.rb +++ b/lib/grape/middleware/error.rb @@ -109,7 +109,7 @@ def rescue_handler_for_grape_exception(klass) return :error_response if klass == Grape::Exceptions::InvalidVersionHeader return unless options[:rescue_grape_exceptions] || !options[:rescue_all] - :error_response + options[:grape_exceptions_rescue_handler] || :default_rescue_handler end def rescue_handler_for_any_class(klass) diff --git a/spec/grape/dsl/request_response_spec.rb b/spec/grape/dsl/request_response_spec.rb index 8546ace00b..8c8ac8d6eb 100644 --- a/spec/grape/dsl/request_response_spec.rb +++ b/spec/grape/dsl/request_response_spec.rb @@ -148,14 +148,34 @@ def self.imbue(key, value) it 'sets rescue all to true' do expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) + expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, nil) subject.rescue_from :grape_exceptions end - it 'sets rescue_grape_exceptions to true' do + it 'sets given proc as rescue handler' do + rescue_handler_proc = proc {} expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) - subject.rescue_from :grape_exceptions + expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, rescue_handler_proc) + subject.rescue_from :grape_exceptions, rescue_handler_proc + end + + it 'sets given block as rescue handler' do + rescue_handler_proc = proc {} + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) + expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, rescue_handler_proc) + subject.rescue_from :grape_exceptions, &rescue_handler_proc + end + + it 'sets a rescue handler declared through :with option' do + with_block = -> { 'hello' } + expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true) + expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true) + expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, an_instance_of(Proc)) + subject.rescue_from :grape_exceptions, with: with_block end + end describe 'list of exceptions is passed' do