Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions lib/xero-ruby/api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ def payroll_au_api
XeroRuby::PayrollAuApi.new(self)
end

# HAND-MAINTAINED: this accessor, the PayrollAuV2Api branches in `call_api`
# and `deserialize`, and `payroll_au_v2_url` in configuration.rb are not yet
# emitted by OpenAPI Generator. Regeneration overwrites this file (see commit
# d400b016), so any codegen bump must also update the generator templates or
# these are silently dropped and `payroll_au_v2_api` raises NoMethodError.
# spec/configuration_spec.rb and spec/api_client_spec.rb guard against that.
def payroll_au_v2_api
@config.base_url = @config.payroll_au_v2_url
XeroRuby::PayrollAuV2Api.new(self)
end

def payroll_nz_api
@config.base_url = @config.payroll_nz_url
XeroRuby::PayrollNzApi.new(self)
Expand Down Expand Up @@ -289,6 +300,8 @@ def call_api(http_method, path, api_client, opts = {})
method_base_url = @config.files_url
when "PayrollAuApi"
method_base_url = @config.payroll_au_url
when "PayrollAuV2Api"
method_base_url = @config.payroll_au_v2_url
when "PayrollNzApi"
method_base_url = @config.payroll_nz_url
when "PayrollUkApi"
Expand All @@ -300,7 +313,9 @@ def call_api(http_method, path, api_client, opts = {})
when "FinanceApi"
method_base_url = @config.finance_url
else
method_base_url = @config.accounting_url
# `connections` and `disconnect` call through with no API class and set
# @config.base_url themselves, so honour it before falling back.
method_base_url = @config.base_url || @config.accounting_url
end

connection = Faraday.new(:url => method_base_url, :ssl => ssl_options) do |conn|
Expand All @@ -314,7 +329,7 @@ def call_api(http_method, path, api_client, opts = {})

begin
response = connection.public_send(http_method.to_sym.downcase) do |req|
build_request(http_method, path, req, opts)
build_request(http_method, path, req, opts, method_base_url)
end

if @config.debugging
Expand Down Expand Up @@ -359,8 +374,8 @@ def return_error(response)
# @option opts [Hash] :form_params Query parameters
# @option opts [Object] :body HTTP body (JSON/XML)
# @return A Faraday Request
def build_request(http_method, path, request, opts = {})
url = build_request_url(path)
def build_request(http_method, path, request, opts = {}, base_url = nil)
url = build_request_url(path, base_url)
http_method = http_method.to_sym.downcase

header_params = @default_headers.merge(opts[:header_params] || {})
Expand Down Expand Up @@ -517,6 +532,8 @@ def convert_to_type(data, return_type, api_client)
XeroRuby::Files.const_get(return_type).build_from_hash(data)
when 'PayrollAuApi'
XeroRuby::PayrollAu.const_get(return_type).build_from_hash(data)
when 'PayrollAuV2Api'
XeroRuby::PayrollAuV2.const_get(return_type).build_from_hash(data)
when 'PayrollNzApi'
XeroRuby::PayrollNz.const_get(return_type).build_from_hash(data)
when 'PayrollUkApi'
Expand Down Expand Up @@ -567,11 +584,16 @@ def sanitize_filename(filename)
filename.gsub(/.*[\/\\]/, '')
end

def build_request_url(path)
if @config.base_url
# `base_url` is resolved per call from the API class that issued the request.
# Falling back to @config.base_url would read shared mutable state that any
# later `client.<other>_api` call has already overwritten, which sends the
# request to the wrong API set while still deserialising into this one.
def build_request_url(path, base_url = nil)
base_url ||= @config.base_url
if base_url
# Add leading and trailing slashes to path
path = "/#{path}".gsub(/\/+/, '/')
@config.base_url + path
base_url + path
else
path
end
Expand Down
5 changes: 5 additions & 0 deletions lib/xero-ruby/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class Configuration
attr_accessor :project_url
attr_accessor :files_url
attr_accessor :payroll_au_url
# HAND-MAINTAINED: not emitted by OpenAPI Generator. Regeneration overwrites
# this file, so a codegen bump must also update the generator templates.
# Guarded by spec/configuration_spec.rb.
attr_accessor :payroll_au_v2_url
attr_accessor :payroll_nz_url
attr_accessor :payroll_uk_url
attr_accessor :app_store_url
Expand Down Expand Up @@ -149,6 +153,7 @@ def initialize
@project_url = 'https://api.xero.com/projects.xro/2.0/'
@files_url = 'https://api.xero.com/files.xro/1.0/'
@payroll_au_url = 'https://api.xero.com/payroll.xro/1.0/'
@payroll_au_v2_url = 'https://api.xero.com/payroll.xro/2.0/'
@payroll_nz_url = 'https://api.xero.com/payroll.xro/2.0/'
@payroll_uk_url = 'https://api.xero.com/payroll.xro/2.0/'
@app_store_url = 'https://api.xero.com/appstore/2.0/'
Expand Down
40 changes: 40 additions & 0 deletions spec/api_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@

api_client.payroll_au_api
expect(api_client.config.base_url).to eq('https://api.xero.com/payroll.xro/1.0/')

expect(api_client.payroll_au_v2_api).to be_a(XeroRuby::PayrollAuV2Api)
expect(api_client.config.base_url).to eq('https://api.xero.com/payroll.xro/2.0/')

api_client.payroll_nz_api
expect(api_client.config.base_url).to eq('https://api.xero.com/payroll.xro/2.0/')
Expand Down Expand Up @@ -221,6 +224,16 @@
expect(data).to be_instance_of(Hash)
expect(data).to eq(:message => 'Hello')
end

it 'deserializes Payroll AU v2 models in their own namespace' do
api_client = XeroRuby::ApiClient.new
headers = { 'Content-Type' => 'application/json' }
response = double('response', headers: headers, body: '{}')

data = api_client.deserialize(response, 'TimesheetObject', 'PayrollAuV2Api')

expect(data).to be_instance_of(XeroRuby::PayrollAuV2::TimesheetObject)
end
end

describe "#object_to_hash modifies a hash from snake_case to PascalCase" do
Expand Down Expand Up @@ -553,4 +566,31 @@
end
end
end

# Regression guards for hand-maintained Payroll AU v2 wiring. OpenAPI Generator
# does not emit these and regeneration overwrites api_client.rb, so if a codegen
# bump drops them these examples fail instead of the SDK silently losing v2.
describe 'payroll AU v2 wiring' do
let(:api_client) { XeroRuby::ApiClient.new }

it 'exposes the payroll_au_v2_api accessor' do
expect(api_client).to respond_to(:payroll_au_v2_api)
expect(api_client.payroll_au_v2_api).to be_a(XeroRuby::PayrollAuV2Api)
end

it 'resolves the request url from the calling API class, not the last-set base_url' do
api_client.payroll_au_v2_api
api_client.payroll_au_api
expect(api_client.config.base_url).to eq('https://api.xero.com/payroll.xro/1.0/')

url = api_client.build_request_url('/Timesheets', api_client.config.payroll_au_v2_url)
expect(url).to start_with('https://api.xero.com/payroll.xro/2.0/')
expect(url).not_to start_with('https://api.xero.com/payroll.xro/1.0/')
end

it 'still falls back to the shared base_url when no per-call base url is given' do
api_client.config.base_url = 'https://api.xero.com'
expect(api_client.build_request_url('/connections/')).to eq('https://api.xero.com/connections/')
end
end
end
17 changes: 17 additions & 0 deletions spec/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,29 @@
expect(config.project_url).to eq('https://api.xero.com/projects.xro/2.0/')
expect(config.files_url).to eq('https://api.xero.com/files.xro/1.0/')
expect(config.payroll_au_url).to eq('https://api.xero.com/payroll.xro/1.0/')
expect(config.payroll_au_v2_url).to eq('https://api.xero.com/payroll.xro/2.0/')
expect(config.payroll_nz_url).to eq('https://api.xero.com/payroll.xro/2.0/')
expect(config.payroll_uk_url).to eq('https://api.xero.com/payroll.xro/2.0/')
expect(config.finance_url).to eq('https://api.xero.com/finance.xro/1.0/')
end
end

# Regression guard for hand-maintained Payroll AU v2 wiring. OpenAPI Generator
# does not emit payroll_au_v2_url and regeneration overwrites configuration.rb,
# so a codegen bump that drops it fails here instead of only surfacing later as
# a NoMethodError from ApiClient#payroll_au_v2_api.
describe 'payroll_au_v2_url' do
it 'is exposed as a reader and a writer' do
expect(config).to respond_to(:payroll_au_v2_url)
expect(config).to respond_to(:payroll_au_v2_url=)
end

it 'defaults to the payroll 2.0 base url and not the v1 one' do
expect(config.payroll_au_v2_url).to eq('https://api.xero.com/payroll.xro/2.0/')
expect(config.payroll_au_v2_url).not_to eq(config.payroll_au_url)
end
end

describe 'config' do
it 'should apply the default configuration options' do
client = XeroRuby::ApiClient.new(credentials: {})
Expand Down