@ogcio/building-blocks-sdk 0.2.11 → 0.2.12

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.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.2.11"
2
+ ".": "0.2.12"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.12](https://github.com/ogcio/building-blocks-sdk/compare/@ogcio/building-blocks-sdk@v0.2.11...@ogcio/building-blocks-sdk@v0.2.12) (2025-02-18)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * check token expiration ([#148](https://github.com/ogcio/building-blocks-sdk/issues/148)) ([63b65fe](https://github.com/ogcio/building-blocks-sdk/commit/63b65fe06e3abac7ad826c8fa2d0dc3fb81798b7))
9
+
3
10
  ## [0.2.11](https://github.com/ogcio/building-blocks-sdk/compare/@ogcio/building-blocks-sdk@v0.2.10...@ogcio/building-blocks-sdk@v0.2.11) (2025-02-13)
4
11
 
5
12
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=base-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-client.test.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/client/clients/base-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,53 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { Messaging } from "../../../client/clients/messaging/index.js";
3
+ import { generateToken } from "../../utils.js";
4
+ describe("Base Client - Get token", () => {
5
+ const baseUrl = "http://fakehost";
6
+ // messaging is not directly base client, but it inherits the get token logic
7
+ it("Will expire in a lot of time", async () => {
8
+ let getTokenFnInvokationNumber = 0;
9
+ const generatedToken = generateToken(10000000000000);
10
+ const getTokenFn = () => {
11
+ getTokenFnInvokationNumber++;
12
+ return Promise.resolve(generatedToken);
13
+ };
14
+ const client = new Messaging({ baseUrl, getTokenFn });
15
+ const token = await client.refreshToken();
16
+ expect(getTokenFnInvokationNumber).toBe(1);
17
+ expect(token).toBe(generatedToken);
18
+ const secondToken = await client.refreshToken();
19
+ expect(getTokenFnInvokationNumber, "It has been invoked even if it is not expired").toBe(1);
20
+ expect(secondToken).toBe(generatedToken);
21
+ });
22
+ it("Is already expired", async () => {
23
+ let getTokenFnInvokationNumber = 0;
24
+ const generatedToken = generateToken(-10000000000000);
25
+ const getTokenFn = () => {
26
+ getTokenFnInvokationNumber++;
27
+ return Promise.resolve(generatedToken);
28
+ };
29
+ const client = new Messaging({ baseUrl, getTokenFn });
30
+ const token = await client.refreshToken();
31
+ expect(getTokenFnInvokationNumber).toBe(1);
32
+ expect(token).toBe(generatedToken);
33
+ const secondToken = await client.refreshToken();
34
+ expect(getTokenFnInvokationNumber, "It has not been invoked even if it is expired").toBe(2);
35
+ expect(secondToken).toBe(generatedToken);
36
+ });
37
+ it("Will expire in few seconds", async () => {
38
+ let getTokenFnInvokationNumber = 0;
39
+ const generatedToken = generateToken(2);
40
+ const getTokenFn = () => {
41
+ getTokenFnInvokationNumber++;
42
+ return Promise.resolve(generatedToken);
43
+ };
44
+ const client = new Messaging({ baseUrl, getTokenFn });
45
+ const token = await client.refreshToken();
46
+ expect(getTokenFnInvokationNumber).toBe(1);
47
+ expect(token).toBe(generatedToken);
48
+ const secondToken = await client.refreshToken();
49
+ expect(getTokenFnInvokationNumber, "It has not been invoked even if it will expire in few seconds").toBe(2);
50
+ expect(secondToken).toBe(generatedToken);
51
+ });
52
+ });
53
+ //# sourceMappingURL=base-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-client.test.js","sourceRoot":"","sources":["../../../../src/__tests__/client/clients/base-client.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAM,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,MAAM,OAAO,GAAG,iBAAiB,CAAC;IAClC,6EAA6E;IAE7E,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,IAAI,0BAA0B,GAAG,CAAC,CAAC;QACnC,MAAM,cAAc,GAAG,aAAa,CAAC,cAAc,CAAC,CAAC;QACrD,MAAM,UAAU,GAAG,GAAG,EAAE;YACtB,0BAA0B,EAAE,CAAC;YAC7B,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACzC,CAAC,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;QAC1C,MAAM,CAAC,0BAA0B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAEnC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;QAChD,MAAM,CACJ,0BAA0B,EAC1B,+CAA+C,CAChD,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACV,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;QAClC,IAAI,0BAA0B,GAAG,CAAC,CAAC;QACnC,MAAM,cAAc,GAAG,aAAa,CAAC,CAAC,cAAc,CAAC,CAAC;QACtD,MAAM,UAAU,GAAG,GAAG,EAAE;YACtB,0BAA0B,EAAE,CAAC;YAC7B,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACzC,CAAC,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;QAC1C,MAAM,CAAC,0BAA0B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAEnC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;QAChD,MAAM,CACJ,0BAA0B,EAC1B,+CAA+C,CAChD,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACV,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,IAAI,0BAA0B,GAAG,CAAC,CAAC;QACnC,MAAM,cAAc,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,GAAG,EAAE;YACtB,0BAA0B,EAAE,CAAC;YAC7B,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACzC,CAAC,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;QAC1C,MAAM,CAAC,0BAA0B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAEnC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;QAChD,MAAM,CACJ,0BAA0B,EAC1B,+DAA+D,CAChE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACV,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import { FeatureFlags } from "../../../../client/clients/featureFlags/index.js";
3
+ import { generateToken } from "../../../utils.js";
3
4
  const enabledOrNot = {
4
5
  "not-enabled": false,
5
6
  enabled: true,
@@ -21,7 +22,7 @@ global.fetch = async () => ({
21
22
  });
22
23
  describe("FeatureFlags", () => {
23
24
  const baseUrl = "http://fakehost";
24
- const getTokenFn = () => Promise.resolve("test-token");
25
+ const getTokenFn = () => Promise.resolve(generateToken(10000));
25
26
  let featureFlags;
26
27
  beforeEach(async () => {
27
28
  featureFlags = new FeatureFlags({ baseUrl, getTokenFn });
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../../../../src/__tests__/client/clients/featureFlags/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,kDAAkD,CAAC;AAEhF,MAAM,YAAY,GAAG;IACnB,aAAa,EAAE,KAAK;IACpB,OAAO,EAAE,IAAI;CACd,CAAC;AAEF,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/B,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;IACnB,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;QACnB,SAAS,EAAE,EAAE,CAAC,EAAE,CACd,CACE,IAA+B,EAC/B,QAAiB,EACjB,cAA6B,EAC7B,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CACxB;KACF,CAAC;IACF,oBAAoB,EAAE,EAAE,CAAC,EAAE,EAAE;CAC9B,CAAC,CAAC,CAAC;AAEJ,IAAI,aAAa,GAAG,EAAE,CAAC;AAEvB,oBAAoB;AACpB,MAAM,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE,CACxB,CAAC;IACC,EAAE,EAAE,IAAI;IACR,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,aAAa;IAC/B,OAAO,EAAE,IAAI,OAAO,EAAE;CACvB,CAAa,CAAC;AAEjB,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,MAAM,OAAO,GAAG,iBAAiB,CAAC;IAClC,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACvD,IAAI,YAA0B,CAAC;IAE/B,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,YAAY,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,aAAa,GAAG,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,eAAe,EAAE,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACrB,IAAI,EAAE,EAAE;YACR,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../../../../src/__tests__/client/clients/featureFlags/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,kDAAkD,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,YAAY,GAAG;IACnB,aAAa,EAAE,KAAK;IACpB,OAAO,EAAE,IAAI;CACd,CAAC;AAEF,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/B,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;IACnB,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;QACnB,SAAS,EAAE,EAAE,CAAC,EAAE,CACd,CACE,IAA+B,EAC/B,QAAiB,EACjB,cAA6B,EAC7B,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CACxB;KACF,CAAC;IACF,oBAAoB,EAAE,EAAE,CAAC,EAAE,EAAE;CAC9B,CAAC,CAAC,CAAC;AAEJ,IAAI,aAAa,GAAG,EAAE,CAAC;AAEvB,oBAAoB;AACpB,MAAM,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE,CACxB,CAAC;IACC,EAAE,EAAE,IAAI;IACR,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,aAAa;IAC/B,OAAO,EAAE,IAAI,OAAO,EAAE;CACvB,CAAa,CAAC;AAEjB,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,MAAM,OAAO,GAAG,iBAAiB,CAAC;IAClC,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,IAAI,YAA0B,CAAC;IAE/B,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,YAAY,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,aAAa,GAAG,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,eAAe,EAAE,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACrB,IAAI,EAAE,EAAE;YACR,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function generateToken(expiresInSeconds: number): string;
2
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/__tests__/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAyB9D"}
@@ -0,0 +1,20 @@
1
+ export function generateToken(expiresInSeconds) {
2
+ // Create JWT header
3
+ const header = {
4
+ alg: "none",
5
+ typ: "JWT",
6
+ };
7
+ // Create JWT payload with mock data
8
+ const payload = {
9
+ sub: "1234567890",
10
+ name: "John Doe",
11
+ email: "john@example.com",
12
+ role: "user",
13
+ exp: Math.floor(Date.now() / 1000) + expiresInSeconds,
14
+ };
15
+ // Encode header and payload
16
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
17
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
18
+ return `${encodedHeader}.${encodedPayload}.`;
19
+ }
20
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/__tests__/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,gBAAwB;IACpD,oBAAoB;IACpB,MAAM,MAAM,GAAG;QACb,GAAG,EAAE,MAAM;QACX,GAAG,EAAE,KAAK;KACX,CAAC;IAEF,oCAAoC;IACpC,MAAM,OAAO,GAAG;QACd,GAAG,EAAE,YAAY;QACjB,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,kBAAkB;QACzB,IAAI,EAAE,MAAM;QACZ,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,gBAAgB;KACtD,CAAC;IAEF,4BAA4B;IAC5B,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAChE,WAAW,CACZ,CAAC;IACF,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAClE,WAAW,CACZ,CAAC;IAEF,OAAO,GAAG,aAAa,IAAI,cAAc,GAAG,CAAC;AAC/C,CAAC"}
@@ -6,6 +6,7 @@ export declare abstract class BaseClient<T extends {}> {
6
6
  private tokenExpiryThresholdMs;
7
7
  protected token?: string;
8
8
  protected tokenExpiryCheckTime: number;
9
+ private refreshLock;
9
10
  protected getTokenFn?: TokenFunction;
10
11
  protected serviceName: SERVICE_NAME | undefined;
11
12
  protected logger?: Logger;
@@ -19,5 +20,9 @@ export declare abstract class BaseClient<T extends {}> {
19
20
  protected getToken(): Promise<string | undefined>;
20
21
  isInitialized(): boolean;
21
22
  hasValidToken(): boolean;
23
+ private toMilliseconds;
24
+ private isTokenExpired;
25
+ private performRefreshToken;
26
+ refreshToken(): Promise<string | undefined>;
22
27
  }
23
28
  //# sourceMappingURL=base-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-client.d.ts","sourceRoot":"","sources":["../../src/client/base-client.ts"],"names":[],"mappings":"AAAA,OAAO,YAAiC,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAM7E,8BAAsB,UAAU,CAAC,CAAC,SAAS,EAAE;IAC3C,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,WAAW,CAAC;IACpB,OAAO,CAAC,sBAAsB,CAAQ;IACtC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,oBAAoB,SAA4B;IAE1D,SAAS,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC;IACrC,SAAS,CAAC,WAAW,EAAE,YAAY,GAAG,SAAS,CAAC;IAChD,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAE1B,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;gBAEzC,EACV,OAAO,EACP,UAAU,EACV,MAAM,GACP,EAAE;QACD,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,aAAa,CAAC;QAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IAyDM,WAAW;cAKF,QAAQ;IASjB,aAAa;IAIb,aAAa;CAGrB"}
1
+ {"version":3,"file":"base-client.d.ts","sourceRoot":"","sources":["../../src/client/base-client.ts"],"names":[],"mappings":"AAAA,OAAO,YAAiC,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAM7E,8BAAsB,UAAU,CAAC,CAAC,SAAS,EAAE;IAC3C,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,WAAW,CAAC;IACpB,OAAO,CAAC,sBAAsB,CAAQ;IACtC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,oBAAoB,SAA4B;IAC1D,OAAO,CAAC,WAAW,CAA0C;IAC7D,SAAS,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC;IACrC,SAAS,CAAC,WAAW,EAAE,YAAY,GAAG,SAAS,CAAC;IAChD,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAE1B,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;gBAEzC,EACV,OAAO,EACP,UAAU,EACV,MAAM,GACP,EAAE;QACD,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,aAAa,CAAC;QAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IA0CM,WAAW;cAKF,QAAQ;IAmBjB,aAAa;IAIb,aAAa;IAIpB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,cAAc;YAIR,mBAAmB;IAQpB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAazD"}
@@ -5,7 +5,8 @@ export class BaseClient {
5
5
  initialized;
6
6
  tokenExpiryThresholdMs = 5000;
7
7
  token;
8
- tokenExpiryCheckTime = Number.POSITIVE_INFINITY;
8
+ tokenExpiryCheckTime = Number.NEGATIVE_INFINITY;
9
+ refreshLock;
9
10
  getTokenFn;
10
11
  serviceName;
11
12
  logger;
@@ -24,20 +25,7 @@ export class BaseClient {
24
25
  this.client = createClient({ baseUrl: this.baseUrl });
25
26
  const authMiddleware = {
26
27
  onRequest: async ({ request }) => {
27
- if ((!this.token || Date.now() >= this.tokenExpiryCheckTime) &&
28
- this.getTokenFn) {
29
- this.token = await this.getTokenFn(this.serviceName);
30
- try {
31
- const { payload } = parseJwtToken(this.token);
32
- const expires = getNumberValueFromObject(payload, "exp");
33
- this.tokenExpiryCheckTime = expires - this.tokenExpiryThresholdMs;
34
- }
35
- catch (err) {
36
- if (this.logger) {
37
- this.logger.warn(err, "failed to set tokenExpiryCheckTime");
38
- }
39
- }
40
- }
28
+ await this.refreshToken();
41
29
  if (this.logger) {
42
30
  const clonedRequest = request.clone();
43
31
  let requestBody = null;
@@ -59,12 +47,22 @@ export class BaseClient {
59
47
  }
60
48
  deleteToken() {
61
49
  this.token = undefined;
62
- this.tokenExpiryCheckTime = Number.POSITIVE_INFINITY;
50
+ this.tokenExpiryCheckTime = Number.NEGATIVE_INFINITY;
63
51
  }
64
52
  async getToken() {
65
- if (this.getTokenFn) {
66
- const token = await this.getTokenFn(this.serviceName);
67
- this.token = token;
53
+ if (!this.getTokenFn) {
54
+ return this.token;
55
+ }
56
+ this.token = await this.getTokenFn(this.serviceName);
57
+ try {
58
+ const { payload } = parseJwtToken(this.token);
59
+ const expires = getNumberValueFromObject(payload, "exp");
60
+ this.tokenExpiryCheckTime =
61
+ this.toMilliseconds(expires) - this.tokenExpiryThresholdMs;
62
+ }
63
+ catch (err) {
64
+ this.deleteToken();
65
+ throw err;
68
66
  }
69
67
  return this.token;
70
68
  }
@@ -72,7 +70,34 @@ export class BaseClient {
72
70
  return this.initialized;
73
71
  }
74
72
  hasValidToken() {
75
- return this.token !== undefined;
73
+ return this.token !== undefined && !this.isTokenExpired();
74
+ }
75
+ toMilliseconds(timestamp) {
76
+ // If timestamp is in seconds (length is 10), convert to milliseconds
77
+ // If already in milliseconds (length is 13), return as is
78
+ return timestamp.toString().length === 10 ? timestamp * 1000 : timestamp;
79
+ }
80
+ isTokenExpired() {
81
+ return this.toMilliseconds(Date.now()) >= this.tokenExpiryCheckTime;
82
+ }
83
+ async performRefreshToken() {
84
+ if (this.hasValidToken()) {
85
+ return this.token;
86
+ }
87
+ return this.getToken();
88
+ }
89
+ async refreshToken() {
90
+ if (this.refreshLock)
91
+ return this.refreshLock;
92
+ let token;
93
+ try {
94
+ this.refreshLock = this.performRefreshToken();
95
+ token = await this.refreshLock;
96
+ }
97
+ finally {
98
+ this.refreshLock = undefined;
99
+ }
100
+ return token;
76
101
  }
77
102
  }
78
103
  //# sourceMappingURL=base-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-client.js","sourceRoot":"","sources":["../../src/client/base-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-client.js","sourceRoot":"","sources":["../../src/client/base-client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ogcio/building-blocks-sdk",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -0,0 +1,68 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { Messaging } from "../../../client/clients/messaging/index.js";
3
+ import { generateToken } from "../../utils.js";
4
+
5
+ describe("Base Client - Get token", () => {
6
+ const baseUrl = "http://fakehost";
7
+ // messaging is not directly base client, but it inherits the get token logic
8
+
9
+ it("Will expire in a lot of time", async () => {
10
+ let getTokenFnInvokationNumber = 0;
11
+ const generatedToken = generateToken(10000000000000);
12
+ const getTokenFn = () => {
13
+ getTokenFnInvokationNumber++;
14
+ return Promise.resolve(generatedToken);
15
+ };
16
+ const client = new Messaging({ baseUrl, getTokenFn });
17
+ const token = await client.refreshToken();
18
+ expect(getTokenFnInvokationNumber).toBe(1);
19
+ expect(token).toBe(generatedToken);
20
+
21
+ const secondToken = await client.refreshToken();
22
+ expect(
23
+ getTokenFnInvokationNumber,
24
+ "It has been invoked even if it is not expired",
25
+ ).toBe(1);
26
+ expect(secondToken).toBe(generatedToken);
27
+ });
28
+
29
+ it("Is already expired", async () => {
30
+ let getTokenFnInvokationNumber = 0;
31
+ const generatedToken = generateToken(-10000000000000);
32
+ const getTokenFn = () => {
33
+ getTokenFnInvokationNumber++;
34
+ return Promise.resolve(generatedToken);
35
+ };
36
+ const client = new Messaging({ baseUrl, getTokenFn });
37
+ const token = await client.refreshToken();
38
+ expect(getTokenFnInvokationNumber).toBe(1);
39
+ expect(token).toBe(generatedToken);
40
+
41
+ const secondToken = await client.refreshToken();
42
+ expect(
43
+ getTokenFnInvokationNumber,
44
+ "It has not been invoked even if it is expired",
45
+ ).toBe(2);
46
+ expect(secondToken).toBe(generatedToken);
47
+ });
48
+
49
+ it("Will expire in few seconds", async () => {
50
+ let getTokenFnInvokationNumber = 0;
51
+ const generatedToken = generateToken(2);
52
+ const getTokenFn = () => {
53
+ getTokenFnInvokationNumber++;
54
+ return Promise.resolve(generatedToken);
55
+ };
56
+ const client = new Messaging({ baseUrl, getTokenFn });
57
+ const token = await client.refreshToken();
58
+ expect(getTokenFnInvokationNumber).toBe(1);
59
+ expect(token).toBe(generatedToken);
60
+
61
+ const secondToken = await client.refreshToken();
62
+ expect(
63
+ getTokenFnInvokationNumber,
64
+ "It has not been invoked even if it will expire in few seconds",
65
+ ).toBe(2);
66
+ expect(secondToken).toBe(generatedToken);
67
+ });
68
+ });
@@ -1,5 +1,6 @@
1
1
  import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import { FeatureFlags } from "../../../../client/clients/featureFlags/index.js";
3
+ import { generateToken } from "../../../utils.js";
3
4
 
4
5
  const enabledOrNot = {
5
6
  "not-enabled": false,
@@ -33,7 +34,7 @@ global.fetch = async () =>
33
34
 
34
35
  describe("FeatureFlags", () => {
35
36
  const baseUrl = "http://fakehost";
36
- const getTokenFn = () => Promise.resolve("test-token");
37
+ const getTokenFn = () => Promise.resolve(generateToken(10000));
37
38
  let featureFlags: FeatureFlags;
38
39
 
39
40
  beforeEach(async () => {
@@ -0,0 +1,26 @@
1
+ export function generateToken(expiresInSeconds: number): string {
2
+ // Create JWT header
3
+ const header = {
4
+ alg: "none",
5
+ typ: "JWT",
6
+ };
7
+
8
+ // Create JWT payload with mock data
9
+ const payload = {
10
+ sub: "1234567890",
11
+ name: "John Doe",
12
+ email: "john@example.com",
13
+ role: "user",
14
+ exp: Math.floor(Date.now() / 1000) + expiresInSeconds,
15
+ };
16
+
17
+ // Encode header and payload
18
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString(
19
+ "base64url",
20
+ );
21
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString(
22
+ "base64url",
23
+ );
24
+
25
+ return `${encodedHeader}.${encodedPayload}.`;
26
+ }
@@ -10,8 +10,8 @@ export abstract class BaseClient<T extends {}> {
10
10
  private initialized;
11
11
  private tokenExpiryThresholdMs = 5000;
12
12
  protected token?: string;
13
- protected tokenExpiryCheckTime = Number.POSITIVE_INFINITY;
14
-
13
+ protected tokenExpiryCheckTime = Number.NEGATIVE_INFINITY;
14
+ private refreshLock: Promise<string | undefined> | undefined;
15
15
  protected getTokenFn?: TokenFunction;
16
16
  protected serviceName: SERVICE_NAME | undefined;
17
17
  protected logger?: Logger;
@@ -41,22 +41,7 @@ export abstract class BaseClient<T extends {}> {
41
41
  this.client = createClient<T>({ baseUrl: this.baseUrl });
42
42
  const authMiddleware: Middleware = {
43
43
  onRequest: async ({ request }) => {
44
- if (
45
- (!this.token || Date.now() >= this.tokenExpiryCheckTime) &&
46
- this.getTokenFn
47
- ) {
48
- this.token = await this.getTokenFn(this.serviceName as SERVICE_NAME);
49
-
50
- try {
51
- const { payload } = parseJwtToken(this.token);
52
- const expires = getNumberValueFromObject(payload, "exp");
53
- this.tokenExpiryCheckTime = expires - this.tokenExpiryThresholdMs;
54
- } catch (err) {
55
- if (this.logger) {
56
- this.logger.warn(err, "failed to set tokenExpiryCheckTime");
57
- }
58
- }
59
- }
44
+ await this.refreshToken();
60
45
 
61
46
  if (this.logger) {
62
47
  const clonedRequest = request.clone();
@@ -85,13 +70,23 @@ export abstract class BaseClient<T extends {}> {
85
70
 
86
71
  public deleteToken() {
87
72
  this.token = undefined;
88
- this.tokenExpiryCheckTime = Number.POSITIVE_INFINITY;
73
+ this.tokenExpiryCheckTime = Number.NEGATIVE_INFINITY;
89
74
  }
90
75
 
91
76
  protected async getToken() {
92
- if (this.getTokenFn) {
93
- const token = await this.getTokenFn(this.serviceName as SERVICE_NAME);
94
- this.token = token;
77
+ if (!this.getTokenFn) {
78
+ return this.token;
79
+ }
80
+
81
+ this.token = await this.getTokenFn(this.serviceName as SERVICE_NAME);
82
+ try {
83
+ const { payload } = parseJwtToken(this.token);
84
+ const expires = getNumberValueFromObject(payload, "exp");
85
+ this.tokenExpiryCheckTime =
86
+ this.toMilliseconds(expires) - this.tokenExpiryThresholdMs;
87
+ } catch (err) {
88
+ this.deleteToken();
89
+ throw err;
95
90
  }
96
91
 
97
92
  return this.token;
@@ -102,6 +97,38 @@ export abstract class BaseClient<T extends {}> {
102
97
  }
103
98
 
104
99
  public hasValidToken() {
105
- return this.token !== undefined;
100
+ return this.token !== undefined && !this.isTokenExpired();
101
+ }
102
+
103
+ private toMilliseconds(timestamp: number) {
104
+ // If timestamp is in seconds (length is 10), convert to milliseconds
105
+ // If already in milliseconds (length is 13), return as is
106
+ return timestamp.toString().length === 10 ? timestamp * 1000 : timestamp;
107
+ }
108
+
109
+ private isTokenExpired() {
110
+ return this.toMilliseconds(Date.now()) >= this.tokenExpiryCheckTime;
111
+ }
112
+
113
+ private async performRefreshToken(): Promise<string | undefined> {
114
+ if (this.hasValidToken()) {
115
+ return this.token;
116
+ }
117
+
118
+ return this.getToken();
119
+ }
120
+
121
+ public async refreshToken(): Promise<string | undefined> {
122
+ if (this.refreshLock) return this.refreshLock;
123
+
124
+ let token: string | undefined;
125
+ try {
126
+ this.refreshLock = this.performRefreshToken();
127
+ token = await this.refreshLock;
128
+ } finally {
129
+ this.refreshLock = undefined;
130
+ }
131
+
132
+ return token;
106
133
  }
107
134
  }
package/vitest.config.mts CHANGED
@@ -11,7 +11,6 @@ export default defineConfig({
11
11
  include: [
12
12
  "**/@(test?(s)|__test?(s)__)/**/*.test.@(js|cjs|mjs|tap|cts|jsx|mts|ts|tsx)",
13
13
  "**/*.@(test?(s)|spec).@(js|cjs|mjs|tap|cts|jsx|mts|ts|tsx)",
14
- "**/test?(s).@(js|cjs|mjs|tap|cts|jsx|mts|ts|tsx)",
15
14
  ],
16
15
  exclude: ["**/@(fixture*(s)|dist|node_modules)/**"],
17
16
  maxConcurrency: 1,