Skip to content
Browse files

handle errors

  • Loading branch information...
1 parent 1d4a67a commit 01844e0b3ae82bbf361045d1bc1a438886dee3ba @nuex nuex committed
Showing with 155 additions and 2 deletions.
  1. +2 −0 lib/ability.rb
  2. +4 −2 lib/ability/client.rb
  3. +67 −0 lib/ability/error.rb
  4. +20 −0 lib/ability/exceptions.rb
  5. +9 −0 test/unit/client_test.rb
  6. +53 −0 test/unit/error_test.rb
View
2 lib/ability.rb
@@ -2,6 +2,8 @@ module Ability; end
require File.expand_path(File.join(File.dirname(__FILE__), "ability", "version"))
require File.expand_path(File.join(File.dirname(__FILE__), "ability", "helpers", "xml_helpers"))
require File.expand_path(File.join(File.dirname(__FILE__), "ability", "helpers", "eligibility_helpers"))
+require File.expand_path(File.join(File.dirname(__FILE__), "ability", "error"))
+require File.expand_path(File.join(File.dirname(__FILE__), "ability", "exceptions"))
require File.expand_path(File.join(File.dirname(__FILE__), "ability", "client"))
require File.expand_path(File.join(File.dirname(__FILE__), "ability", "request"))
require File.expand_path(File.join(File.dirname(__FILE__), "ability", "response"))
View
6 lib/ability/client.rb
@@ -105,9 +105,11 @@ def rest_exec(method, url, payload = nil)
rest_client_opts[:payload] = payload if payload
RestClient::Request.execute(rest_client_opts) do |response, request, result, &block|
+ # If an error code is in the response, raise a ResponseError exception.
+ # Otherwise, return the response normally.
if [400,401,404,405,415,500,503].include?(response.code)
- e = Error::Response.new(response.body)
- e.raise
+ response = Ability::Error::Response.new(xml(response.body), response)
+ response.error.raise
else
response.return!(request, result, &block)
end
View
67 lib/ability/error.rb
@@ -0,0 +1,67 @@
+module Ability
+ class Error
+
+ class << self
+ include Ability::Helpers::XmlHelpers
+
+ # Create an Error instance from a REXML parsed XML document
+ def from_doc(doc, response = nil)
+ error = elem(doc, "//error")
+ code = elem_text(error, "code")
+ message = elem_text(error, "message")
+ details = error.elements.to_a("details/detail").map { |d|
+ k, v = [d.attributes["key"], d.attributes["value"]]
+ { k.to_sym => v }
+ }
+
+ new(code, message, details, response)
+ end
+ end
+
+ attr_reader :code, :message, :details, :response
+
+ def initialize(code, message, details, response = nil)
+ @code = code
+ @message = message
+ @details = details
+ @response = response
+ find_or_create_exception!
+ end
+
+ def raise
+ Kernel.raise exception.new(message, response)
+ end
+
+ private
+
+ attr_reader :error, :exception
+
+ def find_or_create_exception!
+ @exception = Ability.const_defined?(code) ? find_exception : create_exception
+ end
+
+ def find_exception
+ exception_class = Ability.const_get(code)
+ Kernel.raise ExceptionClassClash.new(exception_class) unless exception_class.ancestors.include?(ResponseError)
+ exception_class
+ end
+
+ def create_exception
+ Ability.const_set(code, Class.new(Ability::ResponseError))
+ end
+
+ class Response
+ attr_reader :doc, :response
+
+ def initialize(doc, response = nil)
+ @doc = doc
+ @response = response
+ end
+
+ def error
+ @error ||= Ability::Error.from_doc(doc, self)
+ end
+ end
+
+ end
+end
View
20 lib/ability/exceptions.rb
@@ -0,0 +1,20 @@
+module Ability
+
+ # Parent class of Ability exceptions
+ class AbilityException < StandardError; end
+
+ # Responses with a status between 400 and 503 that contain an <error></error> are
+ # returned in an Ability::Error. The error can be raised via the `raise` method
+ # and an exception will be thrown with the error's XML message.
+ #
+ # All error exceptions are subclasses of ResponseError, allowing all exceptions to
+ # be caught by rescuing ResponseError.
+ class ResponseError < AbilityException
+ attr_reader :response
+ def initialize(message, response)
+ @response = response
+ super(message)
+ end
+ end
+
+end
View
9 test/unit/client_test.rb
@@ -541,6 +541,15 @@ def test_change_password
assert_equal(new_password, @client.password)
end
+ def raises_errors
+ change_password_endpoint = "https://access.abilitynetwork.com/portal/seapi/services/PasswordChange/#{@service_id}"
+ stub_request(:post, change_password_endpoint).with(:body => request_xml(:password_change)).to_return(:status => 400, :body => error_xml(:password_expired))
+
+ assert_raises(Ability::ResponseError) do
+ @client.change_password(@service_id, new_password, :facility_state => "OK", :line_of_business => "PartB")
+ end
+ end
+
private
# Stub an eligibility request
View
53 test/unit/error_test.rb
@@ -0,0 +1,53 @@
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "test_helper"))
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib", "ability"))
+
+class AbilityErrorTest < Test::Unit::TestCase
+
+ def setup
+ @message = "The Medicare application password has expired for the user ID. Reset the password using a terminal emulator or contact the MAC for assistance."
+ end
+
+ def test_error_class_is_automatically_generated
+ Ability.send(:remove_const, :PasswordExpired) if Ability.const_defined?(:PasswordExpired)
+ assert !Ability.const_defined?("PasswordExpired")
+ error = Ability::Error.from_doc(xml(error_xml(:password_expired)))
+ assert Ability.const_defined?("PasswordExpired")
+ end
+
+ def test_error_contains_attributes
+ error = Ability::Error.from_doc(xml(error_xml(:password_expired)))
+ assert_equal @message, error.message
+ assert_equal "PasswordExpired", error.code
+ assert_equal [{ :userId => "someuser" }, { :application => "???" }], error.details
+ end
+
+ def test_error_is_raisable_as_exception
+ error = Ability::Error.from_doc(xml(error_xml(:password_expired)))
+ assert_raises(Ability::PasswordExpired) do
+ error.raise
+ end
+ end
+
+ def test_message_is_passed_along_to_exception
+ error = Ability::Error.from_doc(xml(error_xml(:password_expired)))
+ error.raise
+ rescue Ability::PasswordExpired => e
+ assert_equal @message, e.message
+ end
+
+ def test_response_is_passed_along_to_exception
+ response = Ability::Error::Response.new(xml(error_xml(:password_expired)))
+ response.error.raise
+ rescue Ability::ResponseError => e
+ assert e.response
+ assert_kind_of Ability::Error::Response, e.response
+ assert_equal response.error, e.response.error
+ end
+
+ private
+
+ def xml(raw)
+ REXML::Document.new(raw)
+ end
+
+end

0 comments on commit 01844e0

Please sign in to comment.
Something went wrong with that request. Please try again.