Class: XeroKiwi::Client
- Inherits:
-
Object
- Object
- XeroKiwi::Client
- Defined in:
- lib/xero_kiwi/client.rb
Overview
Entry point for talking to Xero. Holds the OAuth2 token state, knows how to refresh it (when given client credentials), and exposes resource methods that auto-refresh before each request.
# Simple — access token only, no refresh capability.
client = XeroKiwi::Client.new(access_token: "ya29...")
# Full — refresh-capable, with persistence callback.
client = XeroKiwi::Client.new(
access_token: creds.access_token,
refresh_token: creds.refresh_token,
expires_at: creds.expires_at,
client_id: ENV["XERO_CLIENT_ID"],
client_secret: ENV["XERO_CLIENT_SECRET"],
on_token_refresh: ->(token) { creds.update!(token.to_h) }
)
client.token # => XeroKiwi::Token
client.token.expired? # => false
client.refresh_token! # manual force refresh
client.connections # auto-refreshes if expiring; reactive on 401
Defined Under Namespace
Classes: ResponseHandler
Constant Summary collapse
- BASE_URL =
"https://api.xero.com"- DEFAULT_USER_AGENT =
"XeroKiwi/#{XeroKiwi::VERSION} (+https://github.com/douglasgreyling/xero-kiwi)".freeze
- RETRY_STATUSES =
HTTP statuses we treat as transient. faraday-retry honours Retry-After automatically when the status is in this list.
[429, 502, 503, 504].freeze
- DEFAULT_RETRY_OPTIONS =
{ max: 4, interval: 0.5, interval_randomness: 0.5, backoff_factor: 2, retry_statuses: RETRY_STATUSES, methods: %i[get head options put delete post], # Faraday::RetriableResponse is the *internal* signal faraday-retry uses # to flag a status-code retry. It MUST be in this list, or the middleware # can't catch its own retry signal and 429s/503s never get retried. exceptions: [ Faraday::ConnectionFailed, Faraday::TimeoutError, Faraday::RetriableResponse, Errno::ETIMEDOUT ] }.freeze
Instance Attribute Summary collapse
-
#token ⇒ Object
readonly
Returns the value of attribute token.
Instance Method Summary collapse
-
#branding_theme(tenant_id, branding_theme_id) ⇒ Object
Fetches a single Branding Theme by ID for the given tenant.
-
#branding_themes(tenant_id) ⇒ Object
Fetches the Branding Themes for the given tenant.
-
#can_refresh? ⇒ Boolean
True if this client was constructed with refresh credentials AND the current token still carries a refresh_token to use.
-
#connections ⇒ Object
Fetches the list of tenants the current access token has access to.
-
#contact(tenant_id, contact_id) ⇒ Object
Fetches a single Contact by ID for the given tenant.
-
#contact_group(tenant_id, contact_group_id) ⇒ Object
Fetches a single Contact Group by ID for the given tenant.
-
#contact_groups(tenant_id) ⇒ Object
Fetches the Contact Groups for the given tenant.
-
#contacts(tenant_id) ⇒ Object
Fetches the Contacts for the given tenant.
-
#credit_note(tenant_id, credit_note_id) ⇒ Object
Fetches a single Credit Note by ID for the given tenant.
-
#credit_notes(tenant_id) ⇒ Object
Fetches the Credit Notes for the given tenant.
-
#delete_connection(connection_or_id) ⇒ Object
Disconnects a tenant.
-
#initialize(access_token:, refresh_token: nil, expires_at: nil, client_id: nil, client_secret: nil, on_token_refresh: nil, adapter: nil, user_agent: DEFAULT_USER_AGENT, retry_options: {}, throttle: nil) ⇒ Client
constructor
A new instance of Client.
-
#invoice(tenant_id, invoice_id) ⇒ Object
Fetches a single Invoice by ID for the given tenant.
-
#invoices(tenant_id) ⇒ Object
Fetches the Invoices for the given tenant.
-
#online_invoice_url(tenant_id, invoice_id) ⇒ Object
Fetches the online invoice URL for a sales (ACCREC) invoice.
-
#organisation(tenant_id) ⇒ Object
Fetches the Organisation for the given tenant.
-
#overpayment(tenant_id, overpayment_id) ⇒ Object
Fetches a single Overpayment by ID for the given tenant.
-
#overpayments(tenant_id) ⇒ Object
Fetches the Overpayments for the given tenant.
-
#payment(tenant_id, payment_id) ⇒ Object
Fetches a single Payment by ID for the given tenant.
-
#payments(tenant_id) ⇒ Object
Fetches the Payments for the given tenant.
-
#prepayment(tenant_id, prepayment_id) ⇒ Object
Fetches a single Prepayment by ID for the given tenant.
-
#prepayments(tenant_id) ⇒ Object
Fetches the Prepayments for the given tenant.
-
#refresh_token! ⇒ Object
Forces a refresh regardless of expiry.
-
#revoke_token! ⇒ Object
Revokes the current refresh token at Xero, invalidating it and every access token issued from it.
-
#user(tenant_id, user_id) ⇒ Object
Fetches a single User by ID for the given tenant.
-
#users(tenant_id) ⇒ Object
Fetches the Users for the given tenant.
Constructor Details
#initialize(access_token:, refresh_token: nil, expires_at: nil, client_id: nil, client_secret: nil, on_token_refresh: nil, adapter: nil, user_agent: DEFAULT_USER_AGENT, retry_options: {}, throttle: nil) ⇒ Client
Returns a new instance of Client.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/xero_kiwi/client.rb', line 56 def initialize( access_token:, refresh_token: nil, expires_at: nil, client_id: nil, client_secret: nil, on_token_refresh: nil, adapter: nil, user_agent: DEFAULT_USER_AGENT, retry_options: {}, throttle: nil ) @token = Token.new( access_token: access_token, refresh_token: refresh_token, expires_at: expires_at ) @client_id = client_id @client_secret = client_secret @on_token_refresh = on_token_refresh @adapter = adapter @user_agent = user_agent @retry_options = DEFAULT_RETRY_OPTIONS.merge() @throttle = throttle || Throttle::NullLimiter.new @refresh_mutex = Mutex.new end |
Instance Attribute Details
#token ⇒ Object (readonly)
Returns the value of attribute token.
54 55 56 |
# File 'lib/xero_kiwi/client.rb', line 54 def token @token end |
Instance Method Details
#branding_theme(tenant_id, branding_theme_id) ⇒ Object
Fetches a single Branding Theme by ID for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/brandingthemes
389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/xero_kiwi/client.rb', line 389 def branding_theme(tenant_id, branding_theme_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "branding_theme_id is required" if branding_theme_id.nil? || branding_theme_id.to_s.empty? with_authenticated_request do response = http.get("/api.xro/2.0/BrandingThemes/#{branding_theme_id}") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::BrandingTheme.from_response(response.body).first end end |
#branding_themes(tenant_id) ⇒ Object
Fetches the Branding Themes for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/brandingthemes
374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/xero_kiwi/client.rb', line 374 def branding_themes(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/BrandingThemes") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::BrandingTheme.from_response(response.body) end end |
#can_refresh? ⇒ Boolean
True if this client was constructed with refresh credentials AND the current token still carries a refresh_token to use.
439 440 441 |
# File 'lib/xero_kiwi/client.rb', line 439 def can_refresh? !@client_id.nil? && !@client_secret.nil? && @token.refreshable? end |
#connections ⇒ Object
Fetches the list of tenants the current access token has access to. See: developer.xero.com/documentation/best-practices/managing-connections/connections
85 86 87 88 89 90 |
# File 'lib/xero_kiwi/client.rb', line 85 def connections with_authenticated_request do response = http.get("/connections") Connection.from_response(response.body) end end |
#contact(tenant_id, contact_id) ⇒ Object
Fetches a single Contact by ID for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/contacts
156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/xero_kiwi/client.rb', line 156 def contact(tenant_id, contact_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "contact_id is required" if contact_id.nil? || contact_id.to_s.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Contacts/#{contact_id}") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Contact.from_response(response.body).first end end |
#contact_group(tenant_id, contact_group_id) ⇒ Object
Fetches a single Contact Group by ID for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/contactgroups
187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/xero_kiwi/client.rb', line 187 def contact_group(tenant_id, contact_group_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "contact_group_id is required" if contact_group_id.nil? || contact_group_id.to_s.empty? with_authenticated_request do response = http.get("/api.xro/2.0/ContactGroups/#{contact_group_id}") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::ContactGroup.from_response(response.body).first end end |
#contact_groups(tenant_id) ⇒ Object
Fetches the Contact Groups for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/contactgroups
172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/xero_kiwi/client.rb', line 172 def contact_groups(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/ContactGroups") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::ContactGroup.from_response(response.body) end end |
#contacts(tenant_id) ⇒ Object
Fetches the Contacts for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/contacts
141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/xero_kiwi/client.rb', line 141 def contacts(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Contacts") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Contact.from_response(response.body) end end |
#credit_note(tenant_id, credit_note_id) ⇒ Object
Fetches a single Credit Note by ID for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/creditnotes
249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/xero_kiwi/client.rb', line 249 def credit_note(tenant_id, credit_note_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "credit_note_id is required" if credit_note_id.nil? || credit_note_id.to_s.empty? with_authenticated_request do response = http.get("/api.xro/2.0/CreditNotes/#{credit_note_id}") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::CreditNote.from_response(response.body).first end end |
#credit_notes(tenant_id) ⇒ Object
Fetches the Credit Notes for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/creditnotes
234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/xero_kiwi/client.rb', line 234 def credit_notes(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/CreditNotes") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::CreditNote.from_response(response.body) end end |
#delete_connection(connection_or_id) ⇒ Object
Disconnects a tenant. Accepts either a XeroKiwi::Connection (we use its ‘id`) or a raw connection-id string. Returns true on the 204. The access token may still be valid for other connections after this —only the named tenant is detached.
406 407 408 409 410 411 412 413 414 |
# File 'lib/xero_kiwi/client.rb', line 406 def delete_connection(connection_or_id) id = extract_connection_id(connection_or_id) raise ArgumentError, "connection id is required" if id.nil? || id.empty? with_authenticated_request do http.delete("/connections/#{id}") true end end |
#invoice(tenant_id, invoice_id) ⇒ Object
Fetches a single Invoice by ID for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/invoices
342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/xero_kiwi/client.rb', line 342 def invoice(tenant_id, invoice_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "invoice_id is required" if invoice_id.nil? || invoice_id.to_s.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Invoices/#{invoice_id}") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Invoice.from_response(response.body).first end end |
#invoices(tenant_id) ⇒ Object
Fetches the Invoices for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/invoices
327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/xero_kiwi/client.rb', line 327 def invoices(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Invoices") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Invoice.from_response(response.body) end end |
#online_invoice_url(tenant_id, invoice_id) ⇒ Object
Fetches the online invoice URL for a sales (ACCREC) invoice. Returns the URL string, or nil if not available. Cannot be used on DRAFT invoices. See: developer.xero.com/documentation/api/accounting/invoices
358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/xero_kiwi/client.rb', line 358 def online_invoice_url(tenant_id, invoice_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "invoice_id is required" if invoice_id.nil? || invoice_id.to_s.empty? data = with_authenticated_request do http.get("/api.xro/2.0/Invoices/#{invoice_id}/OnlineInvoice") do |req| req.headers["Xero-Tenant-Id"] = tid end end data.body.dig("OnlineInvoices", 0, "OnlineInvoiceUrl") end |
#organisation(tenant_id) ⇒ Object
Fetches the Organisation for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/organisation
95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/xero_kiwi/client.rb', line 95 def organisation(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Organisation") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Organisation.from_response(response.body) end end |
#overpayment(tenant_id, overpayment_id) ⇒ Object
Fetches a single Overpayment by ID for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/overpayments
280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/xero_kiwi/client.rb', line 280 def overpayment(tenant_id, overpayment_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "overpayment_id is required" if overpayment_id.nil? || overpayment_id.to_s.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Overpayments/#{overpayment_id}") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Overpayment.from_response(response.body).first end end |
#overpayments(tenant_id) ⇒ Object
Fetches the Overpayments for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/overpayments
265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/xero_kiwi/client.rb', line 265 def overpayments(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Overpayments") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Overpayment.from_response(response.body) end end |
#payment(tenant_id, payment_id) ⇒ Object
Fetches a single Payment by ID for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/payments
311 312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/xero_kiwi/client.rb', line 311 def payment(tenant_id, payment_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "payment_id is required" if payment_id.nil? || payment_id.to_s.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Payments/#{payment_id}") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Payment.from_response(response.body).first end end |
#payments(tenant_id) ⇒ Object
Fetches the Payments for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/payments
296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/xero_kiwi/client.rb', line 296 def payments(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Payments") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Payment.from_response(response.body) end end |
#prepayment(tenant_id, prepayment_id) ⇒ Object
Fetches a single Prepayment by ID for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/prepayments
218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/xero_kiwi/client.rb', line 218 def prepayment(tenant_id, prepayment_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "prepayment_id is required" if prepayment_id.nil? || prepayment_id.to_s.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Prepayments/#{prepayment_id}") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Prepayment.from_response(response.body).first end end |
#prepayments(tenant_id) ⇒ Object
Fetches the Prepayments for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/prepayments
203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/xero_kiwi/client.rb', line 203 def prepayments(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Prepayments") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::Prepayment.from_response(response.body) end end |
#refresh_token! ⇒ Object
Forces a refresh regardless of expiry. Returns the new Token. Raises TokenRefreshError if refresh credentials are missing or if Xero rejects the refresh.
431 432 433 434 435 |
# File 'lib/xero_kiwi/client.rb', line 431 def refresh_token! raise TokenRefreshError.new(nil, nil, "client has no refresh capability") unless can_refresh? @refresh_mutex.synchronize { perform_refresh } end |
#revoke_token! ⇒ Object
Revokes the current refresh token at Xero, invalidating it and every access token issued from it. Use this for “disconnect Xero” / logout flows. After this call, treat the client as dead — subsequent API calls will 401. The caller is responsible for cleaning up any persisted credential record.
421 422 423 424 425 426 |
# File 'lib/xero_kiwi/client.rb', line 421 def revoke_token! raise TokenRefreshError.new(nil, nil, "client has no refresh capability") unless can_refresh? revoker.revoke_token(refresh_token: @token.refresh_token) true end |
#user(tenant_id, user_id) ⇒ Object
Fetches a single User by ID for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/users
125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/xero_kiwi/client.rb', line 125 def user(tenant_id, user_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? raise ArgumentError, "user_id is required" if user_id.nil? || user_id.to_s.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Users/#{user_id}") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::User.from_response(response.body).first end end |
#users(tenant_id) ⇒ Object
Fetches the Users for the given tenant. Accepts a tenant-id string or a XeroKiwi::Connection (we use its tenant_id). See: developer.xero.com/documentation/api/accounting/users
110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/xero_kiwi/client.rb', line 110 def users(tenant_id) tid = extract_tenant_id(tenant_id) raise ArgumentError, "tenant_id is required" if tid.nil? || tid.empty? with_authenticated_request do response = http.get("/api.xro/2.0/Users") do |req| req.headers["Xero-Tenant-Id"] = tid end Accounting::User.from_response(response.body) end end |