diff --git a/lib/vend/exception.rb b/lib/vend/exception.rb index 1714d47..c11d344 100644 --- a/lib/vend/exception.rb +++ b/lib/vend/exception.rb @@ -8,6 +8,7 @@ def initialize(headers) class BadRequest < HttpError; end class Unauthorized < HttpError; end + class PaymentRequired < HttpError; end class Forbidden < HttpError; end class NotFound < HttpError; end class MethodNotAllowed < HttpError; end @@ -25,6 +26,7 @@ module HttpErrors ERRORS = { 400 => Vend::BadRequest, 401 => Vend::Unauthorized, + 402 => Vend::PaymentRequired, 403 => Vend::Forbidden, 404 => Vend::NotFound, 405 => Vend::MethodNotAllowed, @@ -40,8 +42,21 @@ module HttpErrors }.freeze def throw_http_exception!(code, env) - return unless ERRORS.keys.include? code + return if (200..299).include?(code.to_i) + + response_headers = extract_headers_for_error(env) + error_class = error_class_by(code) + + raise error_class.new(response_headers), env.body + end + + def error_class_by(code) + ERRORS.fetch(code, ::Vend::HttpError) + end + + def extract_headers_for_error(env) response_headers = {} + unless env.body.empty? response_headers = begin Oj.load(env.body, symbol_keys: true) @@ -49,10 +64,12 @@ def throw_http_exception!(code, env) {} end end + unless env[:response_headers] && env[:response_headers]['X-Retry-After'].nil? response_headers[:retry_after] = env[:response_headers]['X-Retry-After'].to_i end - raise ERRORS[code].new(response_headers), env.body + + response_headers end end end diff --git a/spec/vend/unit/exception_spec.rb b/spec/vend/unit/exception_spec.rb index 617c84b..01ac7db 100644 --- a/spec/vend/unit/exception_spec.rb +++ b/spec/vend/unit/exception_spec.rb @@ -1,6 +1,5 @@ RSpec.describe Vend::HttpErrors do let(:dummy_class) { Class.new { extend Vend::HttpErrors } } - let(:code) { 200 } let(:env) { double } let(:body) { {} } let(:headers) { {} } @@ -14,18 +13,73 @@ expect(Vend::HttpErrors::ERRORS).not_to be_nil end - context 'invalid response status' do - context 'when we get a 404' do + context 'when received a successful response' do + it 'does not throw an exception' do + (200..299).each do |code| + expect { dummy_class.throw_http_exception!(code, env) }.not_to raise_exception + end + end + end + + context 'when response code is 0' do + subject { dummy_class.throw_http_exception!(0, env) } + + let(:body) { 'Request timeout' } + + it 'throws the default exception' do + expect { subject }.to raise_exception(Vend::HttpError, body) + end + end + + context 'when response code is not successful' do + context 'when response code is mapped to a known exception class' do + let(:unknown_error_codes) { [*100..199] + [*300..599] - Vend::HttpErrors::ERRORS.keys } + + let(:body) { 'Something web wrong' } + + it 'throws the default exception' do + expect(unknown_error_codes.count).to be_positive + + unknown_error_codes.each do |code| + expect { dummy_class.throw_http_exception!(code, env) }.to raise_exception(Vend::HttpError, body) + end + end + end + + context 'when received code 402' do + let(:code) { 402 } + let(:body) do + <<~HTML + +