Skip to content

Commit

Permalink
Add shared ErrorHandlingResource for all resources.
Browse files Browse the repository at this point in the history
This handles non 200-299 responses by all resources with specific errors, where necessayr and default handlers.
  • Loading branch information
phillbaker committed Oct 6, 2015
1 parent 6d524ec commit 9316989
Show file tree
Hide file tree
Showing 29 changed files with 164 additions and 3 deletions.
14 changes: 13 additions & 1 deletion lib/droplet_kit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,23 @@ module DropletKit

# Utils
autoload :PaginatedResource, 'droplet_kit/paginated_resource'
autoload :ErrorHandlingResourcable, 'droplet_kit/error_handling_resourcable'

# Errors
autoload :ErrorMapping, 'droplet_kit/mappings/error_mapping'
Error = Class.new(StandardError)
FailedCreate = Class.new(DropletKit::Error)
FailedUpdate = Class.new(DropletKit::Error)
RateLimitReached = Class.new(DropletKit::Error)

class RateLimitReached < DropletKit::Error
attr_accessor :limit, :remaining, :reset_at

def limit
@limit.to_i if @limit
end

def remaining
@remaining.to_i if @remaining
end
end
end
19 changes: 19 additions & 0 deletions lib/droplet_kit/error_handling_resourcable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module ErrorHandlingResourcable
def self.included(base)
base.send(:resources) do
default_handler do |response|
if (200...299).include?(response.status)
next
elsif response.status == 429
error = DropletKit::RateLimitReached.new("#{response.status}: #{response.body}")
error.limit = response.headers['RateLimit-Limit']
error.remaining = response.headers['RateLimit-Remaining']
error.reset_at = response.headers['RateLimit-Reset']
raise error
else
raise DropletKit::Error.new("#{response.status}: #{response.body}")
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/droplet_kit/paginated_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def last?

def total_pages
return nil if self.total.nil?

(self.total.to_f / per_page.to_f).ceil
end

Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/account_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class AccountResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
default_handler(:ok) {|r| AccountMapping.extract_single(r.body, :read) }
get '/v2/account' => :info
Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/action_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class ActionResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
action :all, 'GET /v2/actions' do
query_keys :per_page, :page
Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/domain_record_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class DomainRecordResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
action :all, 'GET /v2/domains/:for_domain/records' do
query_keys :per_page, :page
Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/domain_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class DomainResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
action :all, 'GET /v2/domains' do
query_keys :per_page, :page
Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/droplet_action_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class DropletActionResource < ResourceKit::Resource
include ErrorHandlingResourcable

ACTIONS_WITHOUT_INPUT = %w(reboot power_cycle shutdown power_off
power_on password_reset enable_ipv6 disable_backups enable_private_networking)

Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/droplet_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class DropletResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
action :all, 'GET /v2/droplets' do
query_keys :per_page, :page
Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/droplet_upgrade_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class DropletUpgradeResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
action :all, 'GET /v2/droplet_upgrades' do
handler(200) { |response| DropletUpgradeMapping.extract_collection(response.body, :read) }
Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/image_action_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class ImageActionResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
default_handler(422) { |response| ErrorMapping.fail_with(FailedCreate, response.body) }

Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/image_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class ImageResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
action :all, 'GET /v2/images' do
query_keys :page, :per_page, :type, :private
Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/region_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class RegionResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
action :all, 'GET /v2/regions' do
query_keys :per_page, :page
Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/size_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class SizeResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
action :all, 'GET /v2/sizes' do
query_keys :per_page, :page
Expand Down
2 changes: 2 additions & 0 deletions lib/droplet_kit/resources/ssh_key_resource.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module DropletKit
class SSHKeyResource < ResourceKit::Resource
include ErrorHandlingResourcable

resources do
default_handler(:ok, :created) {|r| SSHKeyMapping.extract_single(r.body, :read) }

Expand Down
5 changes: 5 additions & 0 deletions spec/lib/droplet_kit/resources/account_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
expect(account_info.email).to eq(parsed['account']['email'])
expect(account_info.uuid).to eq(parsed['account']['uuid'])
expect(account_info.email_verified).to eq(parsed['account']['email_verified'])
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/account' }
let(:method) { :get }
let(:action) { :info }
end
end
end
7 changes: 7 additions & 0 deletions spec/lib/droplet_kit/resources/action_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,12 @@
expect(action.region.available).to be(true)
expect(action.region.features).to include("virtio", "private_networking", "backups", "ipv6", "metadata")
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/actions/123' }
let(:method) { :get }
let(:action) { :find }
let(:arguments) { { id: 123 } }
end
end
end
7 changes: 7 additions & 0 deletions spec/lib/droplet_kit/resources/domain_record_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
expected_record = DropletKit::DomainRecordMapping.extract_single(response, :read)
expect(resource.find(id: 12, for_domain: 'example.com')).to eq(expected_record)
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/domains/example.com/records/12' }
let(:method) { :get }
let(:action) { :find }
let(:arguments) { { for_domain: 'example.com', id: 123 } }
end
end

describe '#delete' do
Expand Down
7 changes: 7 additions & 0 deletions spec/lib/droplet_kit/resources/domain_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@

expect(resource.find(name: 'example.com')).to eq(expected_domain)
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/domains/example.com' }
let(:method) { :get }
let(:action) { :find }
let(:arguments) { { name: 'example.com' } }
end
end

describe '#delete' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,5 +163,12 @@
expect(returned_action.region.available).to be(true)
expect(returned_action.region.features).to include("virtio", "private_networking", "backups", "ipv6", "metadata")
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/droplets/1066/actions/123' }
let(:method) { :get }
let(:action) { :find }
let(:arguments) { { droplet_id: 1066, id: 123 } }
end
end
end
7 changes: 7 additions & 0 deletions spec/lib/droplet_kit/resources/droplet_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ def check_droplet(droplet)
expect(droplet).to be_kind_of(DropletKit::Droplet)
check_droplet(droplet)
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/droplets/123' }
let(:method) { :get }
let(:action) { :find }
let(:arguments) { { id: 123 } }
end
end

describe '#create' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@

expect(resource.all).to eq(expected)
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/droplet_upgrades' }
let(:method) { :get }
let(:action) { :all }
end
end
end
7 changes: 7 additions & 0 deletions spec/lib/droplet_kit/resources/image_action_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,12 @@
expect(action.region.available).to be(true)
expect(action.region.features).to include("virtio", "private_networking", "backups", "ipv6", "metadata")
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/images/1066/actions/123' }
let(:method) { :get }
let(:action) { :find }
let(:arguments) { { image_id: 1066, id: 123 } }
end
end
end
7 changes: 7 additions & 0 deletions spec/lib/droplet_kit/resources/image_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@
expect(image.regions).to eq(["region--1"])
expect(image.type).to eq("snapshot")
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/images/123' }
let(:method) { :get }
let(:action) { :find }
let(:arguments) { { id: 123 } }
end
end

describe '#delete' do
Expand Down
6 changes: 6 additions & 0 deletions spec/lib/droplet_kit/resources/region_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,11 @@
let(:fixture_path) {'regions/all'}
let(:api_path) {'/v2/regions'}
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/regions' }
let(:method) { :get }
let(:action) { :all }
end
end
end
6 changes: 6 additions & 0 deletions spec/lib/droplet_kit/resources/size_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,11 @@
let(:fixture_path) {'sizes/all'}
let(:api_path) {'/v2/sizes'}
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/sizes' }
let(:method) { :get }
let(:action) { :all }
end
end
end
7 changes: 7 additions & 0 deletions spec/lib/droplet_kit/resources/ssh_key_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@
expect(ssh_key.public_key).to eq("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDZEgsAbWmQF+f8TU3F4fCg4yjVzdKudQbbhGb+qRKP5ju4Yo0Zzneia+oFm4bfzG+ydxUlOlbzq+Tpoj+INFv5 example")
expect(ssh_key.name).to eq("Example Key")
end

it_behaves_like 'resource that handles common errors' do
let(:path) { '/v2/account/keys/123' }
let(:method) { :get }
let(:action) { :find }
let(:arguments) { { id: 123 } }
end
end

describe '#update' do
Expand Down
2 changes: 1 addition & 1 deletion spec/support/request_stub_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def api_fixture(fixture_name)
Pathname.new('./spec/fixtures/').join("#{fixture_name}.json").read
end

def stub_pager_request(total_results = 40)
def stub_pager_request(total_results = 40)
Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/droplets') do |env|
request_count.count += 1
Expand Down
27 changes: 27 additions & 0 deletions spec/support/shared_examples/common_errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
shared_examples_for 'resource that handles common errors' do
let(:arguments) { {} }

it 'handles rate limit' do
response_body = { id: :rate_limit, message: 'Too much!!!' }
stub_do_api(path, method).to_return(body: response_body.to_json, status: 429, headers: {
'RateLimit-Limit' => 1200,
'RateLimit-Remaining' => 1193,
'RateLimit-Reset' => 1402425459,
})

expect { resource.send(action, arguments).to_a }.to raise_exception(DropletKit::Error) do |exception|
expect(exception.message).to match /#{response_body[:message]}/
expect(exception.limit).to eq 1200
expect(exception.remaining).to eq 1193
expect(exception.reset_at).to eq "1402425459"
end
end

it 'handles unauthorized' do
response_body = { id: :unauthorized, message: 'Nuh uh.' }

stub_do_api(path, method).to_return(body: response_body.to_json, status: 401)

expect { resource.send(action, arguments).to_a }.to raise_exception(DropletKit::Error).with_message(/#{response_body[:message]}/)
end
end

0 comments on commit 9316989

Please sign in to comment.