diff --git a/README.md b/README.md index bb4c976..ca5fd80 100644 --- a/README.md +++ b/README.md @@ -246,13 +246,19 @@ handle_api_errors( ### Error IDs Sometimes it's helpful to include IDs with your error responses so that you can correlate a specific error with a record in your logs or bug tracking software. +For this you can use the `error_id` option. +You can either use the UUID error strategy ```ruby -handle_api_errors( - error_id: Proc.new { |error| SecureRandom.uuid } -) +handle_api_errors(error_id: :uuid) +``` + +Or pass a Proc if you need to do something custom. +```ruby +handle_api_errors(error_id: Proc.new { |error| SecureRandom.uuid }) ``` +These will result in: ```json { "error": { diff --git a/lib/api_error_handler.rb b/lib/api_error_handler.rb index 65f927b..bffe117 100644 --- a/lib/api_error_handler.rb +++ b/lib/api_error_handler.rb @@ -1,5 +1,6 @@ require_relative "./api_error_handler/version" require_relative "./api_error_handler/action_controller" +require_relative "./api_error_handler/error_id_generator" Dir[File.join(__dir__, 'api_error_handler', 'serializers', "*.rb")].each { |file| require file } module ApiErrorHandler @@ -19,7 +20,6 @@ module ApiErrorHandler def handle_api_errors(options = {}) format = options.fetch(:format, :json) - status_mapping = ActionDispatch::ExceptionWrapper.rescue_responses error_reporter = options[:error_reporter] serializer_options = SERIALIZER_OPTIONS.merge( options.slice(*SERIALIZER_OPTIONS.keys) @@ -27,13 +27,11 @@ def handle_api_errors(options = {}) serializer_class = options[:serializer] || SERIALIZERS_BY_FORMAT.fetch(format) content_type = options[:content_type] || CONTENT_TYPE_BY_FORMAT[format] - rescue_from StandardError do |error| begin - status = status_mapping[error.class.to_s] + status = ActionDispatch::ExceptionWrapper.rescue_responses[error.class.to_s] - error_id = nil - error_id = options[:error_id].call(error) if options[:error_id] + error_id = ErrorIdGenerator.run(options[:error_id]) error_reporter.call(error, error_id) if error_reporter serializer = serializer_class.new(error, status) diff --git a/lib/api_error_handler/error_id_generator.rb b/lib/api_error_handler/error_id_generator.rb new file mode 100644 index 0000000..e4aa285 --- /dev/null +++ b/lib/api_error_handler/error_id_generator.rb @@ -0,0 +1,18 @@ +require "securerandom" +require_relative "./errors" + +module ApiErrorHandler + class ErrorIdGenerator + def self.run(error_id_option) + if error_id_option.instance_of?(Proc) + error_id_option.call + elsif error_id_option == :uuid + SecureRandom.uuid + elsif error_id_option.nil? + nil + else + raise(InvalidOptionError, "Unable to handle `#{error_id_option}` as argument for the `:error_id` option.") + end + end + end +end diff --git a/lib/api_error_handler/errors.rb b/lib/api_error_handler/errors.rb index a0163c8..0ac25c3 100644 --- a/lib/api_error_handler/errors.rb +++ b/lib/api_error_handler/errors.rb @@ -1,3 +1,5 @@ module ApiErrorHandler - # class UnknownSerializerError < ArgumentError; end + class Error < StandardError; end + + class InvalidOptionError < Error; end end diff --git a/spec/api_error_handler/error_id_generator_spec.rb b/spec/api_error_handler/error_id_generator_spec.rb new file mode 100644 index 0000000..694fbbe --- /dev/null +++ b/spec/api_error_handler/error_id_generator_spec.rb @@ -0,0 +1,23 @@ +require_relative "../../lib/api_error_handler/error_id_generator" + +RSpec.describe ApiErrorHandler::ErrorIdGenerator do + it "Returns the result of the proc if you git it a proc" do + expect(described_class.run(proc { "Result!" })).to eq("Result!") + end + + it "Returns the result of the lambda if you git it a lambda" do + expect(described_class.run(-> { "Result!" })).to eq("Result!") + end + + it "Returns a UUID if you give it :uuid" do + allow(SecureRandom).to receive(:uuid).and_return("Result!") + + expect(described_class.run(:uuid)).to eq("Result!") + end + + it "Raises an error if you give it something it doesn't recognise" do + expect do + described_class.run(:foo) + end.to raise_error(ApiErrorHandler::InvalidOptionError) + end +end