Skip to content
This repository has been archived by the owner on Aug 14, 2024. It is now read-only.

Commit

Permalink
rework of the error handling
Browse files Browse the repository at this point in the history
 add more specific error classes
 access the entire response from the error object
 access the error descriptions directly from the error object
  • Loading branch information
Jannik Graw committed Jan 14, 2016
1 parent ba7d811 commit ebe97fd
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 81 deletions.
6 changes: 1 addition & 5 deletions lib/shipcloud.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require "net/https"
require "json"
require "shipcloud/version"
require "shipcloud/shipcloud_error"

module Shipcloud
API_VERSION = "v1"
Expand Down Expand Up @@ -31,13 +32,8 @@ module Request
autoload :Base, "shipcloud/request/base"
autoload :Connection, "shipcloud/request/connection"
autoload :Info, "shipcloud/request/info"
autoload :Validator, "shipcloud/request/validator"
end

class ShipcloudError < StandardError; end
class AuthenticationError < ShipcloudError; end
class APIError < ShipcloudError; end

class << self
attr_accessor :configuration
end
Expand Down
19 changes: 10 additions & 9 deletions lib/shipcloud/request/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module Shipcloud
module Request
class Base
attr_reader :info
attr_accessor :response

def initialize(info)
@info = info
Expand All @@ -11,23 +10,25 @@ def initialize(info)
def perform
raise AuthenticationError if Shipcloud.api_key.nil?
connection.setup_https
send_request
validator.validated_data_for(response)
response = connection.request
validate_response(response)
JSON.parse(response.body)
rescue JSON::ParserError
raise ShipcloudError.new(response)
end

protected

def send_request
self.response = connection.request
def validate_response(response)
error = ShipcloudError.from_response(response)
if error
raise error
end
end

def connection
@connection ||= Connection.new(info)
end

def validator
@validator ||= Validator.new(info)
end
end
end
end
33 changes: 0 additions & 33 deletions lib/shipcloud/request/validator.rb

This file was deleted.

64 changes: 64 additions & 0 deletions lib/shipcloud/shipcloud_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
module Shipcloud
class ShipcloudError < StandardError
attr_reader :errors, :response

def initialize(response = nil)
@response = response
@errors = parse_errors
error_message = errors.empty? ? response_body : errors
super(error_message)
end

# Returns the appropriate Shipcloud::ShipcloudError subclass based
# on status code
#
# @param [HTTPResponse] response HTTP response
# @return [Shipcloud::ShipcloudError]
def self.from_response(response)
response_code = response.code.to_i
if error_class = error_class_for(response_code)
error_class.new(response)
end
end

private

def self.error_class_for(response_code)
case response_code
when 400 then InvalidRequestError
when 401 then AuthenticationError
when 402 then TooManyRequests
when 404 then NotFoundError
when 400..499 then ClientError
when 500..599 then ServerError
end
end

def parse_errors
return [] unless response_body
data = JSON.parse(response_body)
if data.is_a?(Hash) && data["errors"]
data["errors"]
else
[]
end
rescue JSON::ParserError
[]
end

def response_body
return unless @response
@response.body
end
end

# Raised on errors in the 400-499 range
class ClientError < ShipcloudError; end
class AuthenticationError < ClientError; end
class InvalidRequestError < ClientError; end
class TooManyRequests < ClientError; end
class NotFoundError < ClientError; end

# Raised on errors in the 500-599 range
class ServerError < ShipcloudError; end
end
54 changes: 44 additions & 10 deletions spec/shipcloud/request/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,52 @@
}.to raise_error Shipcloud::AuthenticationError
end

it "performs an https request" do
it "performs an https request and returns a response hash" do
Shipcloud.stub(:api_key).and_return("some key")
connection = stub
validator = stub
Shipcloud::Request::Connection.stub(:new).and_return(connection)
Shipcloud::Request::Validator.stub(:new).and_return(validator)
connection = double
expect(Shipcloud::Request::Connection).to receive(:new).and_return(connection)
expect(connection).to receive(:setup_https)
response = double(code: "200", body: { id: 1 }.to_json)
expect(connection).to receive(:request).and_return(response)

connection.should_receive(:setup_https)
connection.should_receive(:request)
validator.should_receive(:validated_data_for)
data = Shipcloud::Request::Base.new(nil).perform
expect(data).to eq("id" => 1)
end

it "performs an https request and raises an Shipcloud::ClientError if the response "\
"has a 400 status code" do
Shipcloud.stub(:api_key).and_return("some key")
connection = double
expect(Shipcloud::Request::Connection).to receive(:new).and_return(connection)
expect(connection).to receive(:setup_https)
response = double(code: "400", body: { id: 1 }.to_json)
expect(connection).to receive(:request).and_return(response)

expect { Shipcloud::Request::Base.new(nil).perform }.to raise_error(Shipcloud::ClientError)
end

it "performs an https request and raises an Shipcloud::ServerError if the response "\
"has a 500 status code" do
Shipcloud.stub(:api_key).and_return("some key")
connection = double
expect(Shipcloud::Request::Connection).to receive(:new).and_return(connection)
expect(connection).to receive(:setup_https)
response = double(code: "500", body: { id: 1 }.to_json)
expect(connection).to receive(:request).and_return(response)

expect { Shipcloud::Request::Base.new(nil).perform }.to raise_error(Shipcloud::ServerError)
end

it "performs an https request and raises an Shipcloud::ShipcloudError if the body of the "\
"response is not in JSON" do
Shipcloud.stub(:api_key).and_return("some key")
connection = double
expect(Shipcloud::Request::Connection).to receive(:new).and_return(connection)
expect(connection).to receive(:setup_https)
response = double(code: "200", body: "no json")
expect(connection).to receive(:request).and_return(response)

Shipcloud::Request::Base.new(nil).perform
expect { Shipcloud::Request::Base.new(nil).perform }.to raise_error(Shipcloud::ShipcloudError)
end
end
end
end
24 changes: 0 additions & 24 deletions spec/shipcloud/request/validator_spec.rb

This file was deleted.

117 changes: 117 additions & 0 deletions spec/shipcloud/shipcloud_error_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
require "spec_helper"

describe Shipcloud::ShipcloudError do
describe ".from_response" do
context "with a response with status code 400" do
it "returns a Shipcloud::InvalidRequestError" do
expect(Shipcloud::ShipcloudError.from_response(build_response(status_code: 400))).to be_a(
Shipcloud::InvalidRequestError
)
end
end

context "with a response with status code 401" do
it "returns a Shipcloud::AuthenticationError" do
expect(Shipcloud::ShipcloudError.from_response(build_response(status_code: 401))).to be_a(
Shipcloud::AuthenticationError
)
end
end

context "with a response with status code 402" do
it "returns a Shipcloud::TooManyRequests" do
expect(Shipcloud::ShipcloudError.from_response(build_response(status_code: 402))).to be_a(
Shipcloud::TooManyRequests
)
end
end

context "with a response with status code 404" do
it "returns a Shipcloud::NotFoundError" do
expect(Shipcloud::ShipcloudError.from_response(build_response(status_code: 404))).to be_a(
Shipcloud::NotFoundError
)
end
end

context "with a response with a 4xx status code" do
it "returns a Shipcloud::ClientError" do
expect(Shipcloud::ShipcloudError.from_response(build_response(status_code: 400))).to be_a(
Shipcloud::ClientError
)
end
end

context "with a response with a 5xx status code" do
it "returns a Shipcloud::ClientError" do
expect(Shipcloud::ShipcloudError.from_response(build_response(status_code: 500))).to be_a(
Shipcloud::ServerError
)
end
end

context "with a 200 response" do
it "returns nil" do
expect(Shipcloud::ShipcloudError.from_response(build_response(status_code: 200))).to be_nil
end
end
end

describe "#message" do
context "with an errors node in the response body" do
it "returns the errors formated as one string" do
errors = ["error 1", "error 2"]
response = build_response(body: { errors: errors }.to_json)
error = Shipcloud::ShipcloudError.new(response)

expect(error.message).to eq '["error 1", "error 2"]'
end
end
context "without an errors node in the response body" do
it "returns the body of the response" do
error = Shipcloud::ShipcloudError.new(build_response(body: "test"))

expect(error.message).to eq "test"
end
end
end

describe "#errors" do
it "returns the errors node of the response" do
errors = ["error 1", "error 2"]
response = build_response(body: { errors: errors }.to_json)
error = Shipcloud::ShipcloudError.new(response)

expect(error.errors).to eq errors
end
context "with a response that has no errors node" do
it "returns an empty list" do
response = build_response(body: {}.to_json)
error = Shipcloud::ShipcloudError.new(response)

expect(error.errors).to eq []
end
end
context "with a response that has no JSON formated body" do
it "returns an empty list" do
response = build_response(body: "error")
error = Shipcloud::ShipcloudError.new(response)

expect(error.errors).to eq []
end
end
end

describe "#response" do
it "returns the response" do
response = build_response
error = Shipcloud::ShipcloudError.new(response)

expect(error.response).to eq response
end
end

def build_response(status_code: 400, body: nil)
double(code: status_code.to_s, body: body)
end
end

0 comments on commit ebe97fd

Please sign in to comment.