From 8f7236d60b43b698eabda8f73b34bdf3e13c9354 Mon Sep 17 00:00:00 2001 From: Chris Nelson Date: Mon, 27 Jul 2026 14:25:11 +1000 Subject: [PATCH 1/3] Raise a specific XPM authentication error Co-authored-by: ChatGPT Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> --- lib/xpm_ruby.rb | 2 ++ lib/xpm_ruby/connection.rb | 2 ++ spec/xpm_ruby/connection_spec.rb | 32 ++++++++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/xpm_ruby.rb b/lib/xpm_ruby.rb index d18e371..c6555b0 100644 --- a/lib/xpm_ruby.rb +++ b/lib/xpm_ruby.rb @@ -11,6 +11,8 @@ class AccessTokenExpired < Unauthorized; end class Forbidden < Error; end + class AuthenticationUnsuccessful < Forbidden; end + class NotAvailable < Error; end class ConnectionFailed < Error; end diff --git a/lib/xpm_ruby/connection.rb b/lib/xpm_ruby/connection.rb index e9a8c0c..0691ab6 100644 --- a/lib/xpm_ruby/connection.rb +++ b/lib/xpm_ruby/connection.rb @@ -81,6 +81,8 @@ def handle_response(response) end when 403 # this can happen with a bad xero_tenant_id detail = JSON.parse(response.body)["Detail"] + raise AuthenticationUnsuccessful.new(detail) if detail == "AuthenticationUnsuccessful" + raise Forbidden.new(detail) when 500 raise InternalServerError.new(response.reason_phrase) diff --git a/spec/xpm_ruby/connection_spec.rb b/spec/xpm_ruby/connection_spec.rb index 1f34dfc..aa09b1d 100644 --- a/spec/xpm_ruby/connection_spec.rb +++ b/spec/xpm_ruby/connection_spec.rb @@ -22,7 +22,28 @@ module XpmRuby VCR.use_cassette("xpm_ruby/connection/get/bad_tenant") do connection = Connection.new(access_token: access_token, xero_tenant_id: "bad_tenant") - expect { connection.get(endpoint: "staff.api/list") }.to raise_error(XpmRuby::Forbidden, /AuthenticationUnsuccessful/) + expect { connection.get(endpoint: "staff.api/list") } + .to raise_error(XpmRuby::AuthenticationUnsuccessful, /AuthenticationUnsuccessful/) + end + end + end + + context "when XPM returns another forbidden detail" do + before(:each) do + response = instance_double( + Faraday::Response, + status: 403, + body: { Detail: "InsufficientPermissions" }.to_json + ) + allow_any_instance_of(Faraday::Connection).to receive(:get).and_return(response) + end + + it "raises the generic forbidden error" do + connection = Connection.new(access_token: access_token, xero_tenant_id: xero_tenant_id) + + expect { connection.get(endpoint: "staff.api/list") }.to raise_error do |error| + expect(error.class).to eq(XpmRuby::Forbidden) + expect(error.message).to eq("InsufficientPermissions") end end end @@ -113,7 +134,8 @@ module XpmRuby VCR.use_cassette("xpm_ruby/connection/post/bad_tenant") do connection = Connection.new(access_token: access_token, xero_tenant_id: "bad_tenant") - expect { connection.post(endpoint: "job.api/add", data: xml_string) }.to raise_error(XpmRuby::Forbidden, /AuthenticationUnsuccessful/) + expect { connection.post(endpoint: "job.api/add", data: xml_string) } + .to raise_error(XpmRuby::AuthenticationUnsuccessful, /AuthenticationUnsuccessful/) end end end @@ -149,7 +171,8 @@ module XpmRuby VCR.use_cassette("xpm_ruby/connection/put/bad_tenant") do connection = Connection.new(access_token: access_token, xero_tenant_id: "bad_tenant") - expect { connection.put(endpoint: "job.api/update", data: xml_string) }.to raise_error(XpmRuby::Forbidden, /AuthenticationUnsuccessful/) + expect { connection.put(endpoint: "job.api/update", data: xml_string) } + .to raise_error(XpmRuby::AuthenticationUnsuccessful, /AuthenticationUnsuccessful/) end end end @@ -181,7 +204,8 @@ module XpmRuby VCR.use_cassette("xpm_ruby/connection/delete/bad_tenant") do connection = Connection.new(access_token: access_token, xero_tenant_id: "bad_tenant") - expect { connection.delete(endpoint: "client.api/contact", id: contact_id) }.to raise_error(XpmRuby::Forbidden, /AuthenticationUnsuccessful/) + expect { connection.delete(endpoint: "client.api/contact", id: contact_id) } + .to raise_error(XpmRuby::AuthenticationUnsuccessful, /AuthenticationUnsuccessful/) end end end From dfc464e27f85190b97cd2ac11770b2c3e9f5df5a Mon Sep 17 00:00:00 2001 From: Chris Nelson Date: Mon, 27 Jul 2026 14:37:28 +1000 Subject: [PATCH 2/3] Bump xpm_ruby to 0.5.0 Co-authored-by: ChatGPT Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> --- lib/xpm_ruby/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xpm_ruby/version.rb b/lib/xpm_ruby/version.rb index 5a16376..e0f8998 100644 --- a/lib/xpm_ruby/version.rb +++ b/lib/xpm_ruby/version.rb @@ -1,3 +1,3 @@ module XpmRuby - VERSION = "0.4.0".freeze + VERSION = "0.5.0".freeze end From dcf8a434e9d0ca98f95517549c8ad43aacc1e24b Mon Sep 17 00:00:00 2001 From: Chris Nelson Date: Mon, 27 Jul 2026 14:38:21 +1000 Subject: [PATCH 3/3] Use patch version for authentication error Co-authored-by: ChatGPT Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> --- lib/xpm_ruby/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xpm_ruby/version.rb b/lib/xpm_ruby/version.rb index e0f8998..a116001 100644 --- a/lib/xpm_ruby/version.rb +++ b/lib/xpm_ruby/version.rb @@ -1,3 +1,3 @@ module XpmRuby - VERSION = "0.5.0".freeze + VERSION = "0.4.1".freeze end