diff --git a/README.md b/README.md index 9d4e836..b806099 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,10 @@ client.project(project_id).stories(fields: ':default,tasks') # Eage story.comments(fields: ':default,person') # Eagerly get comments and the person that made the comment for a story ``` +## Error Handling +`TrackerApi::Errors::ClientError` is raised for 4xx HTTP status codes +`TrackerApi::Errors::ServerError` is raised for 5xx HTTP status codes + ## Warning Direct mutation of an attribute value skips coercion and dirty tracking. Please use direct assignment or the specialized add_* methods to get expected behavior. @@ -118,6 +122,7 @@ story.save - Add missing resources and endpoints - Add create, update, delete for resources +- Error handling for [error responses](https://www.pivotaltracker.com/help/api#Error_Responses) ## Semantic Versioning http://semver.org/ diff --git a/lib/tracker_api.rb b/lib/tracker_api.rb index 3191fec..9a4f850 100644 --- a/lib/tracker_api.rb +++ b/lib/tracker_api.rb @@ -29,6 +29,8 @@ module TrackerApi module Errors class UnexpectedData < StandardError; end + class ClientError < Error; end + class ServerError < Error; end end module Endpoints diff --git a/lib/tracker_api/client.rb b/lib/tracker_api/client.rb index c092451..f182e5a 100644 --- a/lib/tracker_api/client.rb +++ b/lib/tracker_api/client.rb @@ -248,7 +248,11 @@ def request(method, options = {}) end response rescue Faraday::Error::ClientError => e - raise TrackerApi::Error.new(e) + case e.response[:status] + when 400..499 then raise TrackerApi::Errors::ClientError.new(e) + when 500..599 then raise TrackerApi::Errors::ServerError.new(e) + else raise "Expected 4xx or 5xx HTTP status code" + end end class Pagination diff --git a/test/error_test.rb b/test/error_test.rb new file mode 100644 index 0000000..ac46ddf --- /dev/null +++ b/test/error_test.rb @@ -0,0 +1,41 @@ +require_relative 'minitest_helper' + +describe TrackerApi::Error do + let(:pt_user) { PT_USER_1 } + let(:client) { TrackerApi::Client.new token: pt_user[:token] } + let(:options) { { url: nil, headers: nil } } + + it 'raises ClientError for 4xx HTTP status codes' do + (400..499).each do |status_code| + mock_faraday_error(status_code) + assert_raises TrackerApi::Errors::ClientError do + client.send(:request, :get, options) + end + end + end + + it 'raises ServerError for 5xx HTTP status codes' do + (500..599).each do |status_code| + mock_faraday_error(status_code) + assert_raises TrackerApi::Errors::ServerError do + client.send(:request, :get, options) + end + end + end + + it 'raises RuntimeError for HTTP status codes < 400 and > 500' do + [399, 600].each do |status_code| + mock_faraday_error(status_code) + assert_raises RuntimeError, "Expected 4xx or 5xx HTTP status code" do + client.send(:request, :get, options) + end + end + end + + # Simulate the error Faraday will raise with a specific HTTP status code so + # we can test our rescuing of those errors + def mock_faraday_error(status_code) + ::Faraday::Connection.any_instance.stubs(:get). + raises(::Faraday::Error::ClientError.new(nil, { status: status_code})) + end +end