RedHatInsights/insights-api-common-rails

View on GitHub
lib/insights/api/common/application_controller_mixins/exception_handling.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
module Insights
  module API
    module Common
      module ApplicationControllerMixins
        module ExceptionHandling
          DEFAULT_ERROR_CODE = 400

          def self.included(other)
            other.rescue_from(StandardError, RuntimeError) do |exception|
              rescue_from_handler(exception) do |error_document, exc|
                error_document.add(error_code_from_class(exc).to_s, "#{exc.class}: #{exc.message}")
              end
            end
          end

          def rescue_from_handler(exception)
            errors = Insights::API::Common::ErrorDocument.new.tap do |error_document|
              exception_list_from(exception).each do |exc|
                if api_client_exception?(exc)
                  api_client_errors(exc, error_document)
                elsif custom_exception?(exc)
                  message = fetch_custom_message(exc)
                  error_document.add(error_code_from_class(exc).to_s, message)
                else
                  yield error_document, exc
                end
              end
            end

            render :json => errors.to_h, :status => error_code_from_class(exception)
          end

          def exception_list_from(exception)
            [].tap do |arr|
              until exception.nil?
                arr << exception
                exception = exception.cause
              end
            end
          end

          def custom_exception?(exception)
            Insights::API::Common::CustomExceptions::CUSTOM_EXCEPTION_LIST.include?(exception.class.to_s)
          end

          def fetch_custom_message(exception)
            Insights::API::Common::CustomExceptions.custom_message(exception)
          end

          def error_code_from_class(exception)
            if ActionDispatch::ExceptionWrapper.rescue_responses.key?(exception.class.to_s)
              Rack::Utils.status_code(ActionDispatch::ExceptionWrapper.rescue_responses[exception.class.to_s])
            else
              DEFAULT_ERROR_CODE
            end
          end

          def api_client_exception?(exc)
            exc.respond_to?(:code) && exc.respond_to?(:response_body) && exc.respond_to?(:response_headers) &&
              !exc.response_body.nil?
          end

          def api_client_errors(exc, error_document)
            body = json_parsed_body(exc)
            if body.is_a?(Hash) && body.key?('errors') && body['errors'].is_a?(Array)
              body['errors'].each do |error|
                next unless error.key?('status') && error.key?('detail')

                error_document.add(error['status'], error['detail'])
              end
            else
              error_document.add(exc.code.to_s, exc.message )
            end
          end

          def json_parsed_body(exc)
            JSON.parse(exc.response_body)
          rescue StandardError
            nil
          end
        end
      end
    end
  end
end