-
Notifications
You must be signed in to change notification settings - Fork 163
/
Copy pathxeroClient.ts
121 lines (101 loc) · 3.51 KB
/
xeroClient.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { Issuer, TokenSet, custom } from 'openid-client';
import * as xero from './gen/api';
import request = require('request');
import http = require('http');
export interface IXeroClientConfig {
clientId: string,
clientSecret: string,
redirectUris: string[],
scopes: string[],
state?: string
}
export class XeroClient {
constructor(private readonly config: IXeroClientConfig) {
// need to set access token before use
this.accountingApi = new xero.AccountingApi();
this.buildClient()
}
private tokenSet: TokenSet = new TokenSet
private _tenantIds: string[] = ['']
readonly accountingApi: xero.AccountingApi;
private openIdClient: any; // from openid-client
get tenantIds(): string[] {
return this._tenantIds;
}
async buildClient() {
const issuer = await Issuer.discover('https://identity.xero.com');
this.openIdClient = new issuer.Client({
client_id: this.config.clientId,
client_secret: this.config.clientSecret,
redirect_uris: this.config.redirectUris,
});
this.openIdClient[custom.clock_tolerance] = 5
}
async buildConsentUrl() {
const url = this.openIdClient.authorizationUrl({
redirect_uri: this.config.redirectUris[0],
scope: this.config.scopes.join(' ') || 'openid email profile'
});
return url;
}
async setAccessTokenFromRedirectUri(url: string) {
const params = this.openIdClient.callbackParams(url)
const check = {...params}
this.tokenSet = await this.openIdClient.callback(this.config.redirectUris[0], params, check);
this.setAccessTokenForAllApis();
await this.fetchConnectedTenantIds();
}
async readIdTokenClaims() {
return this.tokenSet.claims();
}
async readTokenSet() {
return this.tokenSet;
}
async setTokenSet(savedTokens: TokenSet) {
this.tokenSet = savedTokens;
this.setAccessTokenForAllApis();
}
async refreshToken() {
if (!this.tokenSet) {
throw new Error('tokenSet is not defined');
}
this.tokenSet = await this.openIdClient.refresh(this.tokenSet.refresh_token);
this.setAccessTokenForAllApis();
await this.fetchConnectedTenantIds();
}
async fetchConnectedTenantIds() {
// retrieve the authorized tenants from api.xero.com/connections
const result = await new Promise<{ response: http.IncomingMessage; body: Array<{ id: string, tenantId: string, tenantType: string }> }>((resolve, reject) => {
request({
method: 'GET',
uri: 'https://api.xero.com/connections',
auth: {
bearer: this.tokenSet.access_token
},
json: true
}, (error, response, body) => {
if (error) {
reject(error);
} else {
if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) {
resolve({ response: response, body: body });
} else {
reject({ response: response, body: body });
}
}
});
});
this._tenantIds = result.body.map(connection => connection.tenantId);
// Requests to the accounting api will look like this:
// let apiResponse = await xeroClient.accountingApi.getInvoices(xeroClient.tenantIds[0]);
}
private setAccessTokenForAllApis() {
const accessToken = this.tokenSet.access_token;
if (typeof accessToken === 'undefined') {
throw new Error('Access token is undefined!');
}
this.accountingApi.accessToken = accessToken;
// this.payrollApi.accessToken = accessToken;
// etc.
}
}