Skip to content

Commit

Permalink
Merge pull request #152 from XeroAPI/timeout_config
Browse files Browse the repository at this point in the history
Fix Thread Safety issue  + Timeout config
  • Loading branch information
SerKnight authored Apr 19, 2021
2 parents e41d75c + 9f8f356 commit cd547e7
Show file tree
Hide file tree
Showing 14 changed files with 152 additions and 30 deletions.
10 changes: 7 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
xero-ruby (2.9.0)
xero-ruby (2.9.1)
faraday (~> 1.0, >= 1.0.1)
json (~> 2.1, >= 2.1.0)

Expand All @@ -12,11 +12,15 @@ GEM
byebug (11.1.3)
coderay (1.1.3)
diff-lcs (1.4.4)
faraday (1.3.0)
faraday (1.4.1)
faraday-excon (~> 1.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
multipart-post (>= 1.2, < 3)
ruby2_keywords
ruby2_keywords (>= 0.0.4)
faraday-excon (1.1.0)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.1.0)
jaro_winkler (1.5.4)
json (2.5.1)
method_source (1.0.0)
Expand Down
2 changes: 1 addition & 1 deletion docs/accounting/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5795,7 +5795,7 @@
} else if (location.includes('php')){
sdkLang = 'php'
sdkName = 'xero-php-oauth2'
} else if (location.includes('dotnet')){
} else if (location.includes('xero-netstandard') || location.includes('csharp')){
sdkLang = 'dotnet'
sdkName = 'Xero-NetStandard'
} else if (location.includes('java')){
Expand Down
2 changes: 1 addition & 1 deletion docs/assets/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1365,7 +1365,7 @@
} else if (location.includes('php')){
sdkLang = 'php'
sdkName = 'xero-php-oauth2'
} else if (location.includes('dotnet')){
} else if (location.includes('xero-netstandard') || location.includes('csharp')){
sdkLang = 'dotnet'
sdkName = 'Xero-NetStandard'
} else if (location.includes('java')){
Expand Down
2 changes: 1 addition & 1 deletion docs/files/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,7 @@
} else if (location.includes('php')){
sdkLang = 'php'
sdkName = 'xero-php-oauth2'
} else if (location.includes('dotnet')){
} else if (location.includes('xero-netstandard') || location.includes('csharp')){
sdkLang = 'dotnet'
sdkName = 'Xero-NetStandard'
} else if (location.includes('java')){
Expand Down
2 changes: 1 addition & 1 deletion docs/payroll_au/DeductionLine.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**deduction_type_id** | **String** | Xero deduction type identifier |
**calculation_type** | [**DeductionTypeCalculationType**](DeductionTypeCalculationType.md) | |
**calculation_type** | [**DeductionTypeCalculationType**](DeductionTypeCalculationType.md) | | [optional]
**amount** | **BigDecimal** | Deduction type amount | [optional]
**percentage** | **BigDecimal** | The Percentage of the Deduction | [optional]
**number_of_units** | **BigDecimal** | Deduction number of units | [optional]
Expand Down
4 changes: 2 additions & 2 deletions docs/payroll_au/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,7 @@
};
defs["DeductionLine"] = {
"title" : "",
"required" : [ "CalculationType", "DeductionTypeID" ],
"required" : [ "DeductionTypeID" ],
"type" : "object",
"properties" : {
"DeductionTypeID" : {
Expand Down Expand Up @@ -3207,7 +3207,7 @@
} else if (location.includes('php')){
sdkLang = 'php'
sdkName = 'xero-php-oauth2'
} else if (location.includes('dotnet')){
} else if (location.includes('xero-netstandard') || location.includes('csharp')){
sdkLang = 'dotnet'
sdkName = 'Xero-NetStandard'
} else if (location.includes('java')){
Expand Down
2 changes: 1 addition & 1 deletion docs/payroll_nz/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3812,7 +3812,7 @@
} else if (location.includes('php')){
sdkLang = 'php'
sdkName = 'xero-php-oauth2'
} else if (location.includes('dotnet')){
} else if (location.includes('xero-netstandard') || location.includes('csharp')){
sdkLang = 'dotnet'
sdkName = 'Xero-NetStandard'
} else if (location.includes('java')){
Expand Down
2 changes: 1 addition & 1 deletion docs/payroll_uk/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3480,7 +3480,7 @@
} else if (location.includes('php')){
sdkLang = 'php'
sdkName = 'xero-php-oauth2'
} else if (location.includes('dotnet')){
} else if (location.includes('xero-netstandard') || location.includes('csharp')){
sdkLang = 'dotnet'
sdkName = 'Xero-NetStandard'
} else if (location.includes('java')){
Expand Down
2 changes: 1 addition & 1 deletion docs/projects/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1442,7 +1442,7 @@
} else if (location.includes('php')){
sdkLang = 'php'
sdkName = 'xero-php-oauth2'
} else if (location.includes('dotnet')){
} else if (location.includes('xero-netstandard') || location.includes('csharp')){
sdkLang = 'dotnet'
sdkName = 'Xero-NetStandard'
} else if (location.includes('java')){
Expand Down
49 changes: 39 additions & 10 deletions lib/xero-ruby/api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ def initialize(config: {}, credentials: {})
@redirect_uri = credentials[:redirect_uri]
@scopes = credentials[:scopes]
@state = credentials[:state]
@config = append_to_default_config(config)
default_config = Configuration.default.clone
@config = append_to_default_config(default_config, config)

@user_agent = "xero-ruby-#{VERSION}"
@default_headers = {
Expand All @@ -47,9 +48,9 @@ def initialize(config: {}, credentials: {})
}
end

def append_to_default_config(user_config)
config = Configuration.default
user_config.each{|k,v| config.send("#{k}=", v) } if user_config
def append_to_default_config(default_config, user_config)
config = default_config
user_config.each{|k,v| config.send("#{k}=", v)}
config
end

Expand Down Expand Up @@ -96,22 +97,30 @@ def payroll_uk_api

# Token Helpers
def token_set
XeroRuby.configure.token_set
@config.token_set
end

def access_token
XeroRuby.configure.access_token
@config.access_token
end

def id_token
@config.id_token
end

def set_token_set(token_set)
# helper to set the token_set on a client once the user
# has a valid token set ( access_token & refresh_token )
XeroRuby.configure.token_set = token_set
@config.token_set = token_set
set_access_token(token_set['access_token'])
end

def set_access_token(access_token)
XeroRuby.configure.access_token = access_token
@config.access_token = access_token
end

def set_id_token(id_token)
@config.id_token = id_token
end

def get_token_set_from_callback(params)
Expand Down Expand Up @@ -182,7 +191,26 @@ def call_api(http_method, path, api_client, opts = {})
:client_key => @config.ssl_client_key
}

connection = Faraday.new(:url => config.base_url, :ssl => ssl_options) do |conn|
case api_client
when "AccountingApi"
method_base_url = @config.accounting_url
when "AssetApi"
method_base_url = @config.asset_url
when "FilesApi"
method_base_url = @config.files_url
when "PayrollAuApi"
method_base_url = @config.payroll_au_url
when "PayrollNzApi"
method_base_url = @config.payroll_nz_url
when "PayrollUkApi"
method_base_url = @config.payroll_uk_url
when "ProjectApi"
method_base_url = @config.project_url
else
method_base_url = @config.accounting_url
end

connection = Faraday.new(:url => method_base_url, :ssl => ssl_options) do |conn|
conn.basic_auth(config.username, config.password)
if opts[:header_params]["Content-Type"] == "multipart/form-data"
conn.request :multipart
Expand Down Expand Up @@ -265,7 +293,8 @@ def build_request(http_method, path, request, opts = {})
end
end
request.headers = header_params
request.options.timeout = @config.timeout
timeout = @config.timeout
request.options.timeout = timeout if timeout > 0
request.body = req_body
request.url url
request.params = query_params
Expand Down
15 changes: 14 additions & 1 deletion lib/xero-ruby/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ class Configuration
# @return [String]
attr_accessor :password

# Defines the access token (Bearer) used with OAuth2.
# Defines the access token (Bearer) used with OAuth2 authorization
attr_accessor :access_token

# Defines OpenID Connect id_token containing Xero user authentication detail
attr_accessor :id_token

# Defines the token set used with OAuth2. May include id/access/refresh token & other meta info.
attr_accessor :token_set
Expand Down Expand Up @@ -146,6 +149,8 @@ def initialize
@payroll_au_url = 'https://api.xero.com/payroll.xro/1.0/'
@payroll_nz_url = 'https://api.xero.com/payroll.xro/2.0/'
@payroll_uk_url = 'https://api.xero.com/payroll.xro/2.0/'
@access_token = nil
@id_token = nil
@api_key = {}
@api_key_prefix = {}
@timeout = 0
Expand Down Expand Up @@ -191,6 +196,14 @@ def base_path=(base_path)
def base_url=(api_url)
@base_url = api_url
end

def access_token=(access_token)
@access_token = access_token
end

def id_token=(id_token)
@id_token = id_token
end

# Gets API key (with prefix if set).
# @param [String] param_name the parameter name of API key auth
Expand Down
5 changes: 0 additions & 5 deletions lib/xero-ruby/models/payroll_au/deduction_line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,13 @@ def list_invalid_properties
invalid_properties.push('invalid value for "deduction_type_id", deduction_type_id cannot be nil.')
end

if @calculation_type.nil?
invalid_properties.push('invalid value for "calculation_type", calculation_type cannot be nil.')
end

invalid_properties
end

# Check to see if the all the properties in the model are valid
# @return true if the model is valid
def valid?
return false if @deduction_type_id.nil?
return false if @calculation_type.nil?
true
end

Expand Down
4 changes: 2 additions & 2 deletions lib/xero-ruby/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
Generated by: https://openapi-generator.tech
OpenAPI Generator version: 4.3.1

The version of the XeroOpenAPI document: 2.10.4
The version of the XeroOpenAPI document: 2.10.5
=end

module XeroRuby
VERSION = '2.9.0'
VERSION = '2.9.1'
end
81 changes: 81 additions & 0 deletions spec/api_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -371,4 +371,85 @@
expect(api_client.sanitize_filename('.\sun.gif')).to eq('sun.gif')
end
end

describe 'thread safety in the XeroClient' do
let(:creds) {{
client_id: 'abc',
client_secret: '123',
redirect_uri: 'https://mydomain.com/callback',
scopes: 'openid profile email accounting.transactions'
}}
let(:api_client_1) {XeroRuby::ApiClient.new(credentials: creds)}
let(:api_client_2) {XeroRuby::ApiClient.new(credentials: creds)}
let(:api_client_3) {XeroRuby::ApiClient.new(credentials: creds)}

let(:tkn_set_1){{id_token: "abc.123.1", access_token: "xxx.yyy.zzz.111"}}
let(:tkn_set_2){{id_token: "efg.456.2", access_token: "xxx.yyy.zzz.222"}}

describe 'when configuration is changed, other instantiations of the client are not affected' do
it 'applies to #set_access_token' do
expect(api_client_1.access_token).to eq(nil)
expect(api_client_2.access_token).to eq(nil)
expect(api_client_3.access_token).to eq(nil)

api_client_1.set_access_token("ACCESS_TOKEN_1")
expect(api_client_1.access_token).to eq("ACCESS_TOKEN_1")
expect(api_client_2.access_token).to eq(nil)
expect(api_client_3.access_token).to eq(nil)

api_client_2.set_access_token("ACCESS_TOKEN_2")
expect(api_client_1.access_token).to eq("ACCESS_TOKEN_1")
expect(api_client_2.access_token).to eq("ACCESS_TOKEN_2")
expect(api_client_3.access_token).to eq(nil)

api_client_3.set_access_token("ACCESS_TOKEN_3")
expect(api_client_1.access_token).to eq("ACCESS_TOKEN_1")
expect(api_client_2.access_token).to eq("ACCESS_TOKEN_2")
expect(api_client_3.access_token).to eq("ACCESS_TOKEN_3")
end

it 'applies to #set_id_token' do
expect(api_client_1.id_token).to eq(nil)
expect(api_client_2.id_token).to eq(nil)

api_client_1.set_id_token("id_token_1")
expect(api_client_1.id_token).to eq("id_token_1")
expect(api_client_2.id_token).to eq(nil)

api_client_2.set_id_token("id_token_2")
expect(api_client_1.id_token).to eq("id_token_1")
expect(api_client_2.id_token).to eq("id_token_2")
end

it 'applies to #set_token_set' do
expect(api_client_1.token_set).to eq(nil)
expect(api_client_2.token_set).to eq(nil)

api_client_1.set_token_set(tkn_set_1)
expect(api_client_1.token_set).to eq(tkn_set_1)
expect(api_client_2.token_set).to eq(nil)

api_client_2.set_token_set(tkn_set_2)
expect(api_client_1.token_set).to eq(tkn_set_1)
expect(api_client_2.token_set).to eq(tkn_set_2)
end

it 'applies to #base_url' do
expect(api_client_1.config.base_url).to eq(nil)
expect(api_client_2.config.base_url).to eq(nil)

api_client_1.accounting_api
expect(api_client_1.config.base_url).to eq(api_client_1.config.accounting_url)
expect(api_client_2.config.base_url).to eq(nil)

api_client_2.files_api
expect(api_client_1.config.base_url).to eq(api_client_1.config.accounting_url)
expect(api_client_2.config.base_url).to eq(api_client_1.config.files_url)

api_client_2.project_api
expect(api_client_1.config.base_url).to eq(api_client_1.config.accounting_url)
expect(api_client_2.config.base_url).to eq(api_client_1.config.project_url)
end
end
end
end

0 comments on commit cd547e7

Please sign in to comment.