From 84b8631dd35285f94624ce6d3c814707f92758c1 Mon Sep 17 00:00:00 2001 From: Anthony Breckner Date: Tue, 18 Aug 2026 10:23:37 +0800 Subject: [PATCH 1/3] Return the assigned job from Job.assign MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XPM's job.api/assign response is a full Job document carrying the authoritative post-assignment state: the assigned staff, manager and partner. Job.assign discarded all of it and returned only the status string, so a caller could learn that a request succeeded but never what XPM actually recorded — leaving no way to tell a partial assignment from a complete one. Return response["Job"] instead, matching add, update, get and applytemplate, which already return the job. The status is not lost as a signal: Connection#handle_response raises ApiError on ERROR, so assign either returns the job or raises. The existing assign cassettes already contain the full payload, so the specs now assert on the confirmed assignment rather than on "OK". Co-authored-by: Claude Opus 5 --- lib/xpm_ruby/job.rb | 2 +- lib/xpm_ruby/version.rb | 2 +- spec/xpm_ruby/job_spec.rb | 18 ++++++++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/xpm_ruby/job.rb b/lib/xpm_ruby/job.rb index 0406274..2ac94ca 100644 --- a/lib/xpm_ruby/job.rb +++ b/lib/xpm_ruby/job.rb @@ -69,7 +69,7 @@ def assign(access_token:, xero_tenant_id:, job_xml:) .new(access_token: access_token, xero_tenant_id: xero_tenant_id) .put(endpoint: "job.api/assign", data: job_xml) - response["Status"] + response["Job"] end def applytemplate(access_token:, xero_tenant_id:, job:) diff --git a/lib/xpm_ruby/version.rb b/lib/xpm_ruby/version.rb index a116001..e0f8998 100644 --- a/lib/xpm_ruby/version.rb +++ b/lib/xpm_ruby/version.rb @@ -1,3 +1,3 @@ module XpmRuby - VERSION = "0.4.1".freeze + VERSION = "0.5.0".freeze end diff --git a/spec/xpm_ruby/job_spec.rb b/spec/xpm_ruby/job_spec.rb index 4e19ea8..4e8c0d9 100644 --- a/spec/xpm_ruby/job_spec.rb +++ b/spec/xpm_ruby/job_spec.rb @@ -161,7 +161,8 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response).to eq("OK") + expect(response["ID"]).to eq("J000032") + expect(response.dig("Assigned", "Staff", "ID")).to eq("859230") end end end @@ -173,7 +174,8 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/remove") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response).to eq("OK") + expect(response["ID"]).to eq("J000032") + expect(response["Assigned"]).to be_nil end end end @@ -187,7 +189,8 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/manager") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response).to eq("OK") + expect(response["ID"]).to eq("J000032") + expect(response.dig("Manager", "ID")).to eq("859230") end end end @@ -199,7 +202,8 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/removemanager") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response).to eq("OK") + expect(response["ID"]).to eq("J000032") + expect(response["Manager"]).to be_nil end end end @@ -213,7 +217,8 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/partner") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response).to eq("OK") + expect(response["ID"]).to eq("J000032") + expect(response.dig("Partner", "ID")).to eq("859230") end end end @@ -225,7 +230,8 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/removepartner") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response).to eq("OK") + expect(response["ID"]).to eq("J000032") + expect(response["Partner"]).to be_nil end end end From 3c1f158315ae6fda20182b5e51696cf72ec5aa01 Mon Sep 17 00:00:00 2001 From: Anthony Breckner Date: Tue, 18 Aug 2026 10:49:39 +0800 Subject: [PATCH 2/3] Sync the lockfile with the 0.5.0 version bump CI installs in frozen mode, so the lockfile has to record the version the gemspec resolves. Co-authored-by: Claude Opus 5 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index b71d2f0..d0ba2c7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - xpm_ruby (0.4.1) + xpm_ruby (0.5.0) activesupport builder dry-types From 6edc8a4cc0ce94daaa0ae264f05a91a816a9848d Mon Sep 17 00:00:00 2001 From: Anthony Breckner Date: Tue, 18 Aug 2026 11:00:53 +0800 Subject: [PATCH 3/3] Return the status alongside the assigned job Digging out "Job" traded one loss for another: a caller checking the outcome could no longer see the status, and neither half is recoverable from the other. Return the whole response so both are available. Co-authored-by: Claude Opus 5 --- lib/xpm_ruby/job.rb | 11 +++++++---- spec/xpm_ruby/job_spec.rb | 30 ++++++++++++++++++------------ 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/xpm_ruby/job.rb b/lib/xpm_ruby/job.rb index 2ac94ca..cb69dd9 100644 --- a/lib/xpm_ruby/job.rb +++ b/lib/xpm_ruby/job.rb @@ -63,13 +63,16 @@ def delete(access_token:, xero_tenant_id:, job:) end # The XML structure for job.assign does not fit in a Hash - # so we need to pass in the XML directly + # so we need to pass in the XML directly. + # + # Returns the whole response — both "Status" and the "Job" describing the + # assignment XPM confirmed — rather than digging out one of them, because a + # caller checking the outcome and a caller reading the assignment both need + # it and neither can recover the other half. def assign(access_token:, xero_tenant_id:, job_xml:) - response = Connection + Connection .new(access_token: access_token, xero_tenant_id: xero_tenant_id) .put(endpoint: "job.api/assign", data: job_xml) - - response["Job"] end def applytemplate(access_token:, xero_tenant_id:, job:) diff --git a/spec/xpm_ruby/job_spec.rb b/spec/xpm_ruby/job_spec.rb index 4e8c0d9..2cea3b8 100644 --- a/spec/xpm_ruby/job_spec.rb +++ b/spec/xpm_ruby/job_spec.rb @@ -161,8 +161,9 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response["ID"]).to eq("J000032") - expect(response.dig("Assigned", "Staff", "ID")).to eq("859230") + expect(response["Status"]).to eq("OK") + expect(response.dig("Job", "ID")).to eq("J000032") + expect(response.dig("Job", "Assigned", "Staff", "ID")).to eq("859230") end end end @@ -174,8 +175,9 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/remove") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response["ID"]).to eq("J000032") - expect(response["Assigned"]).to be_nil + expect(response["Status"]).to eq("OK") + expect(response.dig("Job", "ID")).to eq("J000032") + expect(response.dig("Job", "Assigned")).to be_nil end end end @@ -189,8 +191,9 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/manager") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response["ID"]).to eq("J000032") - expect(response.dig("Manager", "ID")).to eq("859230") + expect(response["Status"]).to eq("OK") + expect(response.dig("Job", "ID")).to eq("J000032") + expect(response.dig("Job", "Manager", "ID")).to eq("859230") end end end @@ -202,8 +205,9 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/removemanager") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response["ID"]).to eq("J000032") - expect(response["Manager"]).to be_nil + expect(response["Status"]).to eq("OK") + expect(response.dig("Job", "ID")).to eq("J000032") + expect(response.dig("Job", "Manager")).to be_nil end end end @@ -217,8 +221,9 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/partner") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response["ID"]).to eq("J000032") - expect(response.dig("Partner", "ID")).to eq("859230") + expect(response["Status"]).to eq("OK") + expect(response.dig("Job", "ID")).to eq("J000032") + expect(response.dig("Job", "Partner", "ID")).to eq("859230") end end end @@ -230,8 +235,9 @@ module XpmRuby VCR.use_cassette("xpm_ruby/job/assign/removepartner") do response = Job.assign(access_token: access_token, xero_tenant_id: xero_tenant_id, job_xml: job_xml) - expect(response["ID"]).to eq("J000032") - expect(response["Partner"]).to be_nil + expect(response["Status"]).to eq("OK") + expect(response.dig("Job", "ID")).to eq("J000032") + expect(response.dig("Job", "Partner")).to be_nil end end end