Skip to content

Commit 2646203

Browse files
Cache promise
1 parent b4ac3dd commit 2646203

File tree

3 files changed

+131
-50
lines changed

3 files changed

+131
-50
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Check Update
2+
concurrency: check
3+
4+
on:
5+
workflow_dispatch:
6+
schedule:
7+
# Automatically run on every Sunday
8+
- cron: '0 0 * * 0'
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v3
16+
with:
17+
token: ${{ secrets.WORKFLOW_SECRET }}
18+
19+
- name: Check for Update
20+
uses: saadmk11/github-actions-version-updater@v0.7.4
21+
with:
22+
token: ${{ secrets.WORKFLOW_SECRET }}
23+
release_types: 'major'

src/wrapper/SquidexClient.ts

Lines changed: 107 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,43 @@ export declare namespace SquidexClient {
1010
clientSecret: string;
1111
environment?: environments.SquidexEnvironment | string;
1212
appName: string;
13+
tokenStore?: TokenStore;
14+
}
15+
16+
export interface TokenStore {
17+
get(): string | undefined;
18+
19+
set(token: string): void;
1320
}
1421
}
1522

16-
export class SquidexClient extends FernClient {
23+
24+
export class SquidexInMemoryTokenStore implements SquidexClient.TokenStore {
1725
private token: string | undefined;
1826

27+
get(): string | undefined {
28+
return this.token;
29+
}
30+
31+
set(token: string): void {
32+
this.token = token;
33+
}
34+
}
35+
36+
export class SquidexStorageTokenStore implements SquidexClient.TokenStore {
37+
constructor(readonly store: Storage = localStorage, readonly key = 'SquidexToken') {
38+
}
39+
40+
get(): string | undefined {
41+
return this.store.getItem(this.key) || undefined;
42+
}
43+
44+
set(token: string): void {
45+
this.store.setItem(this.key, token);
46+
}
47+
}
48+
49+
export class SquidexClient extends FernClient {
1950
public get appName() {
2051
return this.options.appName;
2152
}
@@ -36,60 +67,87 @@ export class SquidexClient extends FernClient {
3667
super({
3768
environment: clientOptions.environment,
3869
appName: clientOptions.appName,
39-
token: async () => {
40-
if (this.token == null) {
41-
const response = await core.fetcher({
42-
url: urlJoin(
43-
this.options.environment ?? environments.SquidexEnvironment.Default,
44-
"/identity-server/connect/token"
45-
),
46-
contentType: "application/x-www-form-urlencoded",
47-
method: "POST",
48-
body: new URLSearchParams({
49-
grant_type: "client_credentials",
50-
client_id: clientOptions.clientId,
51-
client_secret: clientOptions.clientSecret,
52-
scope: "squidex-api",
53-
}),
54-
});
55-
if (response.ok) {
56-
const accessToken = (response.body as any)?.["access_token"];
57-
if (typeof accessToken !== "string") {
70+
token: buildTokenResolver(clientOptions)
71+
});
72+
}
73+
}
74+
75+
function buildTokenResolver(options: SquidexClient.Options) {
76+
const store = options.tokenStore || new SquidexInMemoryTokenStore();
77+
78+
const cachedPromise: {
79+
promise?: Promise<string>
80+
} = {};
81+
82+
return () => {
83+
const promise = (cachedPromise.promise ||= (async () => {
84+
try {
85+
let token = store.get();
86+
87+
if (token != null) {
88+
return token;
89+
}
90+
91+
const response = await core.fetcher({
92+
url: urlJoin(
93+
options.environment ?? environments.SquidexEnvironment.Default,
94+
"/identity-server/connect/token"
95+
),
96+
contentType: "application/x-www-form-urlencoded",
97+
body: new URLSearchParams({
98+
grant_type: "client_credentials",
99+
client_id: options.clientId,
100+
client_secret: options.clientSecret,
101+
scope: "squidex-api",
102+
}),
103+
method: "POST",
104+
});
105+
106+
if (response.ok) {
107+
const accessToken = (response.body as any)?.["access_token"];
108+
if (typeof accessToken !== "string") {
109+
throw new errors.SquidexError({
110+
message: "Token is not a string",
111+
});
112+
}
113+
token = accessToken;
114+
} else {
115+
switch (response.error.reason) {
116+
case "non-json":
58117
throw new errors.SquidexError({
59-
message: "Token is not a string",
118+
message: 'Token request does not return a valid JSON object.',
119+
statusCode: response.error.statusCode,
120+
body: response.error.rawBody,
60121
});
61-
}
62-
this.token = accessToken;
63-
} else {
64-
switch (response.error.reason) {
65-
case "non-json":
66-
throw new errors.SquidexError({
67-
message: 'Token request does not return a valid JSON object.',
68-
statusCode: response.error.statusCode,
69-
body: response.error.rawBody,
70-
});
71-
case "status-code":
72-
throw new errors.SquidexError({
73-
message: `Token request returns invalid status code: ${response.error.statusCode}.`,
74-
statusCode: response.error.statusCode,
75-
body: response.error['body'],
76-
});
77-
case "unknown":
78-
throw new errors.SquidexError({
79-
message: response.error.errorMessage,
80-
});
81-
case "timeout":
82-
throw new errors.SquidexTimeoutError();
83-
}
122+
case "status-code":
123+
throw new errors.SquidexError({
124+
message: `Token request returns invalid status code: ${response.error.statusCode}.`,
125+
statusCode: response.error.statusCode,
126+
body: response.error['body'],
127+
});
128+
case "unknown":
129+
throw new errors.SquidexError({
130+
message: response.error.errorMessage,
131+
});
132+
case "timeout":
133+
throw new errors.SquidexTimeoutError();
84134
}
85135
}
86-
if (this.token == null) {
136+
137+
store.set(token);
138+
139+
if (token == null) {
87140
throw new errors.SquidexError({
88141
message: "Token is null despite trying to fetch",
89142
});
90143
}
91-
return this.token;
92-
},
93-
});
144+
145+
return token;
146+
} finally {
147+
cachedPromise.promise = undefined;
148+
}
149+
})());
150+
151+
return promise;
94152
}
95-
}
153+
}

tests/_utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { SquidexClient } from "../src/wrapper/SquidexClient";
44

55
let singleClient: { client: SquidexClient, create: (app: string) => SquidexClient };
66

7-
export function getClient(print = false) {
7+
export function getClient() {
88
if (singleClient) {
99
return singleClient;
1010
}

0 commit comments

Comments
 (0)