emissions-api-sdk 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Client.js +69 -9
- package/dist/Constants.js +2 -1
- package/dist/coverage/clover.xml +72 -57
- package/dist/coverage/coverage-final.json +4 -4
- package/dist/coverage/lcov-report/index.html +18 -18
- package/dist/coverage/lcov-report/src/Client.ts.html +242 -50
- package/dist/coverage/lcov-report/src/Constants.ts.html +7 -4
- package/dist/coverage/lcov-report/src/api/Calculation.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/EconomicActivity.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/Factor.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/FactorSets.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/Fugitive.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/Location.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/Metadata.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/Mobile.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/RealEstate.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/Stationary.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/TransportationAndDistribution.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/TypeRecommender.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/Usage.ts.html +1 -1
- package/dist/coverage/lcov-report/src/api/index.html +1 -1
- package/dist/coverage/lcov-report/src/index.html +21 -21
- package/dist/coverage/lcov-report/src/request.ts.html +1 -1
- package/dist/coverage/lcov-report/src/utils.ts.html +5 -5
- package/dist/coverage/lcov-report/test/index.html +1 -1
- package/dist/coverage/lcov-report/test/mocks/CommonRequest.ts.html +1 -1
- package/dist/coverage/lcov-report/test/mocks/FactorRequest.ts.html +1 -1
- package/dist/coverage/lcov-report/test/mocks/GenericCalculationRequest.ts.html +1 -1
- package/dist/coverage/lcov-report/test/mocks/LocationRequest.ts.html +1 -1
- package/dist/coverage/lcov-report/test/mocks/SearchRequest.ts.html +1 -1
- package/dist/coverage/lcov-report/test/mocks/index.html +1 -1
- package/dist/coverage/lcov-report/test/testUtils.ts.html +4 -4
- package/dist/coverage/lcov.info +161 -121
- package/dist/types/Client.d.ts +26 -0
- package/dist/types/Constants.d.ts +1 -0
- package/dist/types/interfaces/Config.d.ts +6 -0
- package/docs/reference.html +8 -2
- package/docs/searchindex.js +1 -1
- package/package.json +2 -2
- package/src/Client.ts +79 -15
- package/src/Constants.ts +1 -0
- package/src/interfaces/Config.ts +7 -0
- package/test/client.test.ts +93 -1
- package/test/tokenRefresh.test.ts +91 -1
- package/.secrets.baseline +0 -314
package/src/Client.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import { ClientConfig } from "./interfaces/Config";
|
|
3
3
|
import { findExpiryTime } from "./utils";
|
|
4
|
-
import { API_DOMAIN, TOKEN_GENERATION_API, CLIENT_SOURCE_EXCEL, CLIENT_SOURCE_SDK } from "./Constants";
|
|
4
|
+
import { API_DOMAIN, TOKEN_GENERATION_API, PAT_TOKEN_EXCHANGE_API, CLIENT_SOURCE_EXCEL, CLIENT_SOURCE_SDK } from "./Constants";
|
|
5
5
|
|
|
6
6
|
export class Client {
|
|
7
7
|
private static instance: Client | null = null;
|
|
8
8
|
|
|
9
9
|
private token: string;
|
|
10
10
|
private readonly apiKey: string;
|
|
11
|
+
private readonly patToken: string;
|
|
11
12
|
private readonly clientId: string;
|
|
12
13
|
private readonly orgId: string;
|
|
13
14
|
private expiresAt: number;
|
|
@@ -21,28 +22,30 @@ export class Client {
|
|
|
21
22
|
config: ClientConfig,
|
|
22
23
|
isUserProvidedToken = false
|
|
23
24
|
) {
|
|
24
|
-
const { apiKey="", clientId, orgId="", host, authUrl, isExcelAddIn=false } = config;
|
|
25
|
+
const { apiKey="", patToken="", clientId, orgId="", host, authUrl, isExcelAddIn=false } = config;
|
|
25
26
|
this.token = token;
|
|
26
27
|
this.clientId = clientId;
|
|
27
28
|
if (isUserProvidedToken) {
|
|
28
29
|
this.apiKey = "";
|
|
30
|
+
this.patToken = "";
|
|
29
31
|
this.orgId = "";
|
|
30
32
|
} else {
|
|
31
33
|
this.apiKey = apiKey;
|
|
34
|
+
this.patToken = patToken;
|
|
32
35
|
this.orgId = orgId;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
const exp = findExpiryTime(token);
|
|
36
39
|
this.expiresAt = exp;
|
|
37
40
|
this.domain = host ?? API_DOMAIN;
|
|
38
|
-
this.tokenDomain = authUrl ?? TOKEN_GENERATION_API;
|
|
41
|
+
this.tokenDomain = authUrl ?? (patToken ? PAT_TOKEN_EXCHANGE_API : TOKEN_GENERATION_API);
|
|
39
42
|
this.isUserProvidedToken = isUserProvidedToken;
|
|
40
43
|
this.clientSource = isExcelAddIn === true ? CLIENT_SOURCE_EXCEL : CLIENT_SOURCE_SDK;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
/**
|
|
44
47
|
* Initializes and returns a Client instance with the provided configuration.
|
|
45
|
-
*
|
|
48
|
+
*
|
|
46
49
|
* @static
|
|
47
50
|
* @param {ClientConfig} config - The client configuration object
|
|
48
51
|
* @return {Promise<void>} A promise that resolves when the client is initialized
|
|
@@ -50,7 +53,8 @@ export class Client {
|
|
|
50
53
|
* - custom "host" is provided without "authUrl"
|
|
51
54
|
* - token is provided without "clientId"
|
|
52
55
|
* - apiKey is provided without "clientId" or "orgId"
|
|
53
|
-
*
|
|
56
|
+
* - patToken is provided without "clientId" or with "orgId"
|
|
57
|
+
*
|
|
54
58
|
* @example
|
|
55
59
|
* // Initialize with authentication URL
|
|
56
60
|
* await Client.getClient({
|
|
@@ -59,19 +63,25 @@ export class Client {
|
|
|
59
63
|
* clientId: 'client123',
|
|
60
64
|
* orgId: 'org456'
|
|
61
65
|
* });
|
|
62
|
-
*
|
|
66
|
+
*
|
|
63
67
|
* // Initialize with existing token
|
|
64
68
|
* await Client.getClient({
|
|
65
69
|
* token: 'existing-jwt-token',
|
|
66
70
|
* clientId: 'client123'
|
|
67
71
|
* });
|
|
68
|
-
*
|
|
72
|
+
*
|
|
69
73
|
* // Initialize with API key
|
|
70
74
|
* await Client.getClient({
|
|
71
75
|
* apiKey: 'your-api-key',
|
|
72
76
|
* clientId: 'client123',
|
|
73
77
|
* orgId: 'org456'
|
|
74
78
|
* });
|
|
79
|
+
*
|
|
80
|
+
* // Initialize with PAT token
|
|
81
|
+
* await Client.getClient({
|
|
82
|
+
* patToken: 'your-personal-access-token',
|
|
83
|
+
* clientId: 'client123'
|
|
84
|
+
* });
|
|
75
85
|
*/
|
|
76
86
|
|
|
77
87
|
public static async getClient(config: ClientConfig): Promise<void> {
|
|
@@ -90,14 +100,28 @@ export class Client {
|
|
|
90
100
|
'If apiKey is provided , "clientId" and "OrgId" must also be provided.'
|
|
91
101
|
);
|
|
92
102
|
}
|
|
103
|
+
if(config.patToken && !config.clientId){
|
|
104
|
+
throw new Error(
|
|
105
|
+
'If patToken is provided, "clientId" must also be provided.'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
if(config.patToken && config.orgId){
|
|
109
|
+
throw new Error(
|
|
110
|
+
'orgId should not be provided when using patToken.'
|
|
111
|
+
);
|
|
112
|
+
}
|
|
93
113
|
let token: string;
|
|
94
114
|
let isUserProvidedToken = false;
|
|
95
115
|
|
|
96
116
|
if (config.token && config.clientId) {
|
|
97
117
|
token = config.token;
|
|
98
118
|
isUserProvidedToken = true;
|
|
99
|
-
} else
|
|
100
|
-
|
|
119
|
+
} else if (config.patToken && config.clientId) {
|
|
120
|
+
token = await Client.requestTokenFromPAT(config);
|
|
121
|
+
} else {
|
|
122
|
+
token = await Client.requestToken(config);
|
|
123
|
+
}
|
|
124
|
+
Client.instance = new Client(token, config, isUserProvidedToken);
|
|
101
125
|
}
|
|
102
126
|
|
|
103
127
|
/**
|
|
@@ -158,12 +182,21 @@ export class Client {
|
|
|
158
182
|
const now = Math.floor(Date.now() / 1000);
|
|
159
183
|
if (this.expiresAt - now < 60) {
|
|
160
184
|
console.log("[SDK] Refreshing token...");
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
185
|
+
let token: string;
|
|
186
|
+
if (this.patToken) {
|
|
187
|
+
token = await Client.requestTokenFromPAT({
|
|
188
|
+
patToken: this.patToken,
|
|
189
|
+
clientId: this.clientId,
|
|
190
|
+
authUrl: this.tokenDomain,
|
|
191
|
+
});
|
|
192
|
+
} else {
|
|
193
|
+
token = await Client.requestToken({
|
|
194
|
+
apiKey: this.apiKey,
|
|
195
|
+
clientId: this.clientId,
|
|
196
|
+
orgId: this.orgId,
|
|
197
|
+
authUrl: this.tokenDomain,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
167
200
|
this.token = token;
|
|
168
201
|
this.expiresAt = findExpiryTime(token);
|
|
169
202
|
}
|
|
@@ -249,4 +282,35 @@ export class Client {
|
|
|
249
282
|
if (!res.data) throw new Error("Token response is empty");
|
|
250
283
|
return String(res.data).trim();
|
|
251
284
|
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Requests an authentication token by exchanging a Personal Access Token (PAT).
|
|
288
|
+
*
|
|
289
|
+
* @private
|
|
290
|
+
* @static
|
|
291
|
+
* @param {ClientConfig} config - The client configuration containing PAT and client details
|
|
292
|
+
* @return {Promise<string>} A promise that resolves to the authentication token string
|
|
293
|
+
* @throws {Error} Throws an error if the token response is empty
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* // Internal usage within the Client class
|
|
297
|
+
* const token = await Client.requestTokenFromPAT({
|
|
298
|
+
* patToken: 'your-personal-access-token',
|
|
299
|
+
* clientId: 'client123',
|
|
300
|
+
* authUrl: 'https://custom-auth.example.com/exchange'
|
|
301
|
+
* });
|
|
302
|
+
*/
|
|
303
|
+
private static async requestTokenFromPAT(config: ClientConfig): Promise<string> {
|
|
304
|
+
const tokenUrl = config.authUrl ?? PAT_TOKEN_EXCHANGE_API;
|
|
305
|
+
const res = await axios.post(tokenUrl, null, {
|
|
306
|
+
headers: {
|
|
307
|
+
"X-IBM-Client-Id": `saascore-${config.clientId}`,
|
|
308
|
+
"X-IBM-Envizi-Pat": config.patToken,
|
|
309
|
+
accept: "application/json",
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
if (!res.data) throw new Error("Token response is empty");
|
|
314
|
+
return String(res.data).trim();
|
|
315
|
+
}
|
|
252
316
|
}
|
package/src/Constants.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const API_DOMAIN = "https://api.ibm.com/ghgemissions/run";
|
|
2
2
|
export const TOKEN_GENERATION_API = "https://api.ibm.com/saascore/run/authentication-retrieve/api-key";
|
|
3
|
+
export const PAT_TOKEN_EXCHANGE_API = "https://api.ibm.com/saascore/run/envizi-auth/exchange";
|
|
3
4
|
export const MOBILE_API_PATH = "/v3/carbon/mobile";
|
|
4
5
|
export const LOCATION_API_PATH = "/v3/carbon/location";
|
|
5
6
|
export const FUGITIVE_API_PATH = "/v3/carbon/fugitive";
|
package/src/interfaces/Config.ts
CHANGED
|
@@ -70,6 +70,13 @@ export interface ClientConfig {
|
|
|
70
70
|
*/
|
|
71
71
|
token?: string;
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Optional Personal Access Token (PAT) for authentication.
|
|
75
|
+
* If provided, it will be exchanged for a JWT token.
|
|
76
|
+
* Requires clientId to be provided.
|
|
77
|
+
*/
|
|
78
|
+
patToken?: string;
|
|
79
|
+
|
|
73
80
|
/**
|
|
74
81
|
* Optional flag indicating whether the client is running as an Excel Add-In.
|
|
75
82
|
* This may affect certain behaviors like authentication flows.
|
package/test/client.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Client } from "../src/Client";
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
import MockAdapter from "axios-mock-adapter";
|
|
4
|
-
import { TOKEN_GENERATION_API } from "../src/Constants";
|
|
4
|
+
import { TOKEN_GENERATION_API, PAT_TOKEN_EXCHANGE_API } from "../src/Constants";
|
|
5
5
|
import { ClientConfig } from "../src/interfaces/Config";
|
|
6
6
|
|
|
7
7
|
const mock = new MockAdapter(axios);
|
|
@@ -57,6 +57,27 @@ describe("Client initialization and Header Authorization", () => {
|
|
|
57
57
|
'If apiKey is provided , "clientId" and "OrgId" must also be provided.'
|
|
58
58
|
);
|
|
59
59
|
});
|
|
60
|
+
|
|
61
|
+
it("Should throw validation error for missing clientId with patToken", async () => {
|
|
62
|
+
await expect(
|
|
63
|
+
Client.getClient({ patToken: "pat-token" } as unknown as ClientConfig)
|
|
64
|
+
).rejects.toThrow(
|
|
65
|
+
'If patToken is provided, "clientId" must also be provided.'
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("Should throw validation error when orgId is provided with patToken", async () => {
|
|
70
|
+
await expect(
|
|
71
|
+
Client.getClient({
|
|
72
|
+
patToken: "pat-token",
|
|
73
|
+
clientId: "client123",
|
|
74
|
+
orgId: "org456"
|
|
75
|
+
} as ClientConfig)
|
|
76
|
+
).rejects.toThrow(
|
|
77
|
+
'orgId should not be provided when using patToken.'
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
60
81
|
it("should not call refresh token if userProvidedToken is set and set token to userProvided", async () => {
|
|
61
82
|
const base64Header = Buffer.from(
|
|
62
83
|
JSON.stringify({ alg: "HS256", typ: "JWT" })
|
|
@@ -79,4 +100,75 @@ describe("Client initialization and Header Authorization", () => {
|
|
|
79
100
|
});
|
|
80
101
|
expect(spy).not.toHaveBeenCalled();
|
|
81
102
|
});
|
|
103
|
+
|
|
104
|
+
it("should initialize client with PAT token and return auth header", async () => {
|
|
105
|
+
const base64Header = Buffer.from(
|
|
106
|
+
JSON.stringify({ alg: "HS256", typ: "JWT" })
|
|
107
|
+
).toString("base64url");
|
|
108
|
+
const base64Payload = Buffer.from(
|
|
109
|
+
JSON.stringify({ exp: Math.floor(Date.now() / 1000) + 600 })
|
|
110
|
+
).toString("base64url");
|
|
111
|
+
const mockToken = `${base64Header}.${base64Payload}.Signature`;
|
|
112
|
+
|
|
113
|
+
mock.onPost(PAT_TOKEN_EXCHANGE_API).reply(200, mockToken);
|
|
114
|
+
|
|
115
|
+
await Client.getClient({
|
|
116
|
+
patToken: "test-pat-token",
|
|
117
|
+
clientId: "test-client-id",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const instance = Client.getInstance();
|
|
121
|
+
expect(instance.getAuthHeader()).toEqual({
|
|
122
|
+
Authorization: `Bearer ${mockToken}`,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("should initialize client with PAT token and custom authUrl", async () => {
|
|
127
|
+
const base64Header = Buffer.from(
|
|
128
|
+
JSON.stringify({ alg: "HS256", typ: "JWT" })
|
|
129
|
+
).toString("base64url");
|
|
130
|
+
const base64Payload = Buffer.from(
|
|
131
|
+
JSON.stringify({ exp: Math.floor(Date.now() / 1000) + 600 })
|
|
132
|
+
).toString("base64url");
|
|
133
|
+
const mockToken = `${base64Header}.${base64Payload}.Signature`;
|
|
134
|
+
|
|
135
|
+
const customAuthUrl = "https://custom-auth.example.com/exchange";
|
|
136
|
+
mock.onPost(customAuthUrl).reply(200, mockToken);
|
|
137
|
+
|
|
138
|
+
await Client.getClient({
|
|
139
|
+
patToken: "test-pat-token",
|
|
140
|
+
clientId: "test-client-id",
|
|
141
|
+
authUrl: customAuthUrl,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const instance = Client.getInstance();
|
|
145
|
+
expect(instance.getAuthHeader()).toEqual({
|
|
146
|
+
Authorization: `Bearer ${mockToken}`,
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should call requestTokenFromPAT with correct headers", async () => {
|
|
151
|
+
const base64Header = Buffer.from(
|
|
152
|
+
JSON.stringify({ alg: "HS256", typ: "JWT" })
|
|
153
|
+
).toString("base64url");
|
|
154
|
+
const base64Payload = Buffer.from(
|
|
155
|
+
JSON.stringify({ exp: Math.floor(Date.now() / 1000) + 600 })
|
|
156
|
+
).toString("base64url");
|
|
157
|
+
const mockToken = `${base64Header}.${base64Payload}.Signature`;
|
|
158
|
+
|
|
159
|
+
mock.onPost(PAT_TOKEN_EXCHANGE_API).reply((config) => {
|
|
160
|
+
expect(config.headers?.["X-IBM-Client-Id"]).toBe("saascore-test-client-id");
|
|
161
|
+
expect(config.headers?.["X-IBM-Envizi-Pat"]).toBe("test-pat-token");
|
|
162
|
+
expect(config.headers?.["Accept"]).toBe("application/json");
|
|
163
|
+
return [200, mockToken];
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
await Client.getClient({
|
|
167
|
+
patToken: "test-pat-token",
|
|
168
|
+
clientId: "test-client-id",
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const instance = Client.getInstance();
|
|
172
|
+
expect(instance).toBeDefined();
|
|
173
|
+
});
|
|
82
174
|
});
|
|
@@ -3,7 +3,7 @@ import * as utils from "../src/utils";
|
|
|
3
3
|
import mockAxios from "axios";
|
|
4
4
|
import MockAdapter from "axios-mock-adapter";
|
|
5
5
|
import { generateMockjwt } from "./testUtils";
|
|
6
|
-
import { TOKEN_GENERATION_API } from "../src/Constants";
|
|
6
|
+
import { TOKEN_GENERATION_API, PAT_TOKEN_EXCHANGE_API } from "../src/Constants";
|
|
7
7
|
|
|
8
8
|
const mock = new MockAdapter(mockAxios);
|
|
9
9
|
describe("Token refreshing", () => {
|
|
@@ -49,4 +49,94 @@ describe("Token refreshing", () => {
|
|
|
49
49
|
await instance.refreshToken();
|
|
50
50
|
expect(spy).not.toHaveBeenCalledTimes(1);
|
|
51
51
|
});
|
|
52
|
+
|
|
53
|
+
it("should refresh PAT token when it is about to expire", async () => {
|
|
54
|
+
const shortLiveToken = generateMockjwt(1);
|
|
55
|
+
const refreshedToken = generateMockjwt(600);
|
|
56
|
+
|
|
57
|
+
mock
|
|
58
|
+
.onPost(PAT_TOKEN_EXCHANGE_API)
|
|
59
|
+
.replyOnce(200, shortLiveToken)
|
|
60
|
+
.onPost(PAT_TOKEN_EXCHANGE_API)
|
|
61
|
+
.replyOnce(200, refreshedToken);
|
|
62
|
+
|
|
63
|
+
jest
|
|
64
|
+
.spyOn(utils, "findExpiryTime")
|
|
65
|
+
.mockReturnValueOnce(Math.floor(Date.now() / 1000) + 1)
|
|
66
|
+
.mockReturnValueOnce(Math.floor(Date.now() / 1000) + 600);
|
|
67
|
+
|
|
68
|
+
await Client.getClient({
|
|
69
|
+
patToken: "test-pat-token",
|
|
70
|
+
clientId: "test-client-id",
|
|
71
|
+
});
|
|
72
|
+
const instance = Client.getInstance();
|
|
73
|
+
|
|
74
|
+
const spy = jest.spyOn(Client as any, "requestTokenFromPAT");
|
|
75
|
+
|
|
76
|
+
await instance.refreshToken();
|
|
77
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
78
|
+
expect(spy).toHaveBeenCalledWith({
|
|
79
|
+
patToken: "test-pat-token",
|
|
80
|
+
clientId: "test-client-id",
|
|
81
|
+
authUrl: PAT_TOKEN_EXCHANGE_API,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should not refresh PAT token if it is still valid", async () => {
|
|
86
|
+
const freshToken = generateMockjwt(600);
|
|
87
|
+
mock.onPost(PAT_TOKEN_EXCHANGE_API).reply(200, freshToken);
|
|
88
|
+
|
|
89
|
+
const spy = jest.spyOn(Client as any, "requestTokenFromPAT");
|
|
90
|
+
|
|
91
|
+
jest
|
|
92
|
+
.spyOn(utils, "findExpiryTime")
|
|
93
|
+
.mockReturnValue(Math.floor(Date.now() / 1000) + 600);
|
|
94
|
+
|
|
95
|
+
await Client.getClient({
|
|
96
|
+
patToken: "test-pat-token",
|
|
97
|
+
clientId: "test-client-id",
|
|
98
|
+
});
|
|
99
|
+
const instance = Client.getInstance();
|
|
100
|
+
|
|
101
|
+
spy.mockClear(); // Clear the initial call from getClient
|
|
102
|
+
|
|
103
|
+
await instance.refreshToken();
|
|
104
|
+
expect(spy).not.toHaveBeenCalled();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should refresh PAT token with custom authUrl", async () => {
|
|
108
|
+
const shortLiveToken = generateMockjwt(1);
|
|
109
|
+
const refreshedToken = generateMockjwt(600);
|
|
110
|
+
const customAuthUrl = "https://custom-auth.example.com/exchange";
|
|
111
|
+
|
|
112
|
+
mock
|
|
113
|
+
.onPost(customAuthUrl)
|
|
114
|
+
.replyOnce(200, shortLiveToken)
|
|
115
|
+
.onPost(customAuthUrl)
|
|
116
|
+
.replyOnce(200, refreshedToken);
|
|
117
|
+
|
|
118
|
+
const spy = jest.spyOn(Client as any, "requestTokenFromPAT");
|
|
119
|
+
|
|
120
|
+
jest
|
|
121
|
+
.spyOn(utils, "findExpiryTime")
|
|
122
|
+
.mockReturnValueOnce(Math.floor(Date.now() / 1000) + 1)
|
|
123
|
+
.mockReturnValueOnce(Math.floor(Date.now() / 1000) + 600);
|
|
124
|
+
|
|
125
|
+
await Client.getClient({
|
|
126
|
+
patToken: "test-pat-token",
|
|
127
|
+
clientId: "test-client-id",
|
|
128
|
+
authUrl: customAuthUrl,
|
|
129
|
+
});
|
|
130
|
+
const instance = Client.getInstance();
|
|
131
|
+
|
|
132
|
+
spy.mockClear(); // Clear the initial call from getClient
|
|
133
|
+
|
|
134
|
+
await instance.refreshToken();
|
|
135
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
136
|
+
expect(spy).toHaveBeenCalledWith({
|
|
137
|
+
patToken: "test-pat-token",
|
|
138
|
+
clientId: "test-client-id",
|
|
139
|
+
authUrl: customAuthUrl,
|
|
140
|
+
});
|
|
141
|
+
});
|
|
52
142
|
});
|