@ogcio/building-blocks-sdk 0.0.1
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/.husky/commit-msg +1 -0
- package/.husky/pre-push +1 -0
- package/.nvmrc +1 -0
- package/.vscode/settings.json +20 -0
- package/biome.jsonc +45 -0
- package/commitlint.config.js +6 -0
- package/dist/client/auth/index.d.ts +4 -0
- package/dist/client/auth/index.d.ts.map +1 -0
- package/dist/client/auth/index.js +83 -0
- package/dist/client/auth/index.js.map +1 -0
- package/dist/client/base-client.d.ts +18 -0
- package/dist/client/base-client.d.ts.map +1 -0
- package/dist/client/base-client.js +71 -0
- package/dist/client/base-client.js.map +1 -0
- package/dist/client/clients/messaging/index.d.ts +2407 -0
- package/dist/client/clients/messaging/index.d.ts.map +1 -0
- package/dist/client/clients/messaging/index.js +430 -0
- package/dist/client/clients/messaging/index.js.map +1 -0
- package/dist/client/clients/messaging/schema.d.ts +3823 -0
- package/dist/client/clients/messaging/schema.d.ts.map +1 -0
- package/dist/client/clients/messaging/schema.js +2 -0
- package/dist/client/clients/messaging/schema.js.map +1 -0
- package/dist/client/clients/payments/index.d.ts +2294 -0
- package/dist/client/clients/payments/index.d.ts.map +1 -0
- package/dist/client/clients/payments/index.js +217 -0
- package/dist/client/clients/payments/index.js.map +1 -0
- package/dist/client/clients/payments/schema.d.ts +2534 -0
- package/dist/client/clients/payments/schema.d.ts.map +1 -0
- package/dist/client/clients/payments/schema.js +2 -0
- package/dist/client/clients/payments/schema.js.map +1 -0
- package/dist/client/clients/profile/index.d.ts +1364 -0
- package/dist/client/clients/profile/index.d.ts.map +1 -0
- package/dist/client/clients/profile/index.js +102 -0
- package/dist/client/clients/profile/index.js.map +1 -0
- package/dist/client/clients/profile/schema.d.ts +1429 -0
- package/dist/client/clients/profile/schema.d.ts.map +1 -0
- package/dist/client/clients/profile/schema.js +2 -0
- package/dist/client/clients/profile/schema.js.map +1 -0
- package/dist/client/clients/scheduler/index.d.ts +14 -0
- package/dist/client/clients/scheduler/index.d.ts.map +1 -0
- package/dist/client/clients/scheduler/index.js +14 -0
- package/dist/client/clients/scheduler/index.js.map +1 -0
- package/dist/client/clients/scheduler/schema.d.ts +114 -0
- package/dist/client/clients/scheduler/schema.d.ts.map +1 -0
- package/dist/client/clients/scheduler/schema.js +2 -0
- package/dist/client/clients/scheduler/schema.js.map +1 -0
- package/dist/client/clients/upload/index.d.ts +390 -0
- package/dist/client/clients/upload/index.d.ts.map +1 -0
- package/dist/client/clients/upload/index.js +93 -0
- package/dist/client/clients/upload/index.js.map +1 -0
- package/dist/client/clients/upload/schema.d.ts +564 -0
- package/dist/client/clients/upload/schema.d.ts.map +1 -0
- package/dist/client/clients/upload/schema.js +2 -0
- package/dist/client/clients/upload/schema.js.map +1 -0
- package/dist/client/utils/client-utils.d.ts +15 -0
- package/dist/client/utils/client-utils.d.ts.map +1 -0
- package/dist/client/utils/client-utils.js +14 -0
- package/dist/client/utils/client-utils.js.map +1 -0
- package/dist/clients-configurations/clients-configuration.json +92 -0
- package/dist/clients-configurations/read-configuration-file.d.ts +22 -0
- package/dist/clients-configurations/read-configuration-file.d.ts.map +1 -0
- package/dist/clients-configurations/read-configuration-file.js +47 -0
- package/dist/clients-configurations/read-configuration-file.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +63 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/get-absolute-path.d.ts +3 -0
- package/dist/utils/get-absolute-path.d.ts.map +1 -0
- package/dist/utils/get-absolute-path.js +8 -0
- package/dist/utils/get-absolute-path.js.map +1 -0
- package/package.json +44 -0
- package/src/cli/cli-utils.ts +25 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/outdated-clients-command.ts +148 -0
- package/src/cli/update-clients-command.ts +298 -0
- package/src/client/auth/index.ts +131 -0
- package/src/client/base-client.ts +97 -0
- package/src/client/clients/messaging/index.ts +683 -0
- package/src/client/clients/messaging/open-api-definition.json +8394 -0
- package/src/client/clients/messaging/schema.ts +3822 -0
- package/src/client/clients/payments/index.ts +349 -0
- package/src/client/clients/payments/open-api-definition.json +6094 -0
- package/src/client/clients/payments/schema.ts +2533 -0
- package/src/client/clients/profile/index.ts +182 -0
- package/src/client/clients/profile/open-api-definition.json +2876 -0
- package/src/client/clients/profile/schema.ts +1428 -0
- package/src/client/clients/scheduler/index.ts +28 -0
- package/src/client/clients/scheduler/open-api-definition.json +120 -0
- package/src/client/clients/scheduler/schema.ts +113 -0
- package/src/client/clients/upload/index.ts +129 -0
- package/src/client/clients/upload/open-api-definition.json +985 -0
- package/src/client/clients/upload/schema.ts +563 -0
- package/src/client/utils/client-utils.ts +50 -0
- package/src/clients-configurations/clients-configuration.json +92 -0
- package/src/clients-configurations/read-configuration-file.ts +68 -0
- package/src/index.ts +43 -0
- package/src/logto-node.d.ts +12 -0
- package/src/types/index.ts +82 -0
- package/src/utils/get-absolute-path.ts +10 -0
- package/tsconfig.json +18 -0
- package/tsconfig.prod.json +4 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import openapiTS, { astToString } from "openapi-typescript";
|
|
4
|
+
import { parse } from "yaml";
|
|
5
|
+
import {
|
|
6
|
+
type ConfigurationBuildingBlock,
|
|
7
|
+
OpenAPIFileFormats,
|
|
8
|
+
readConfigurationFile,
|
|
9
|
+
} from "../clients-configurations/read-configuration-file.js";
|
|
10
|
+
import getAbsolutePath from "../utils/get-absolute-path.js";
|
|
11
|
+
import {
|
|
12
|
+
CLIENTS_ROOT_FOLDER_PATH,
|
|
13
|
+
OLD_OPEN_API_DEFINITION_FILE_NAME,
|
|
14
|
+
OLD_SCHEMA_FILE_NAME,
|
|
15
|
+
OPEN_API_DEFINITION_FILE_NAME,
|
|
16
|
+
SCHEMA_FILE_NAME,
|
|
17
|
+
getOpenApiDefinitionFileContent,
|
|
18
|
+
} from "./cli-utils.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* This command, starting from a json configuration file,
|
|
22
|
+
* downloads the open-API definition file,
|
|
23
|
+
* stores it in a folder with the building block name,
|
|
24
|
+
* then creates the Typescript schema and stores it in the same folder
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
type FileBackup = {
|
|
28
|
+
oldPath: string | null;
|
|
29
|
+
newPath: string | null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function log(message: string, ...params: unknown[]): void {
|
|
33
|
+
if (params.length === 0) {
|
|
34
|
+
console.log(`Update Clients: ${message}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
console.log(`Update Clients: ${message}`, params);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function storeOpenApiDefinitionFile({
|
|
41
|
+
inputBuildingBlock,
|
|
42
|
+
dryRun,
|
|
43
|
+
}: {
|
|
44
|
+
inputBuildingBlock: ConfigurationBuildingBlock;
|
|
45
|
+
dryRun: boolean;
|
|
46
|
+
}): Promise<{
|
|
47
|
+
filePath: string;
|
|
48
|
+
fileContent: string;
|
|
49
|
+
buildingBlockFolder: string;
|
|
50
|
+
fileBackup: FileBackup;
|
|
51
|
+
}> {
|
|
52
|
+
log(`${inputBuildingBlock.name} - Downloading Open API definition file`);
|
|
53
|
+
const downloadedFileContent = await getOpenApiDefinitionFileContent(
|
|
54
|
+
inputBuildingBlock.openApiDefinitionUrl,
|
|
55
|
+
);
|
|
56
|
+
log(`${inputBuildingBlock.name} - Open API definition file downloaded`);
|
|
57
|
+
const openApiParsed =
|
|
58
|
+
inputBuildingBlock.openApiDefinitionFormat === OpenAPIFileFormats.JSON
|
|
59
|
+
? JSON.parse(downloadedFileContent)
|
|
60
|
+
: parse(downloadedFileContent);
|
|
61
|
+
log(`${inputBuildingBlock.name} - Open API definition file parsed`);
|
|
62
|
+
|
|
63
|
+
const serviceFolderPath = getAbsolutePath(
|
|
64
|
+
CLIENTS_ROOT_FOLDER_PATH,
|
|
65
|
+
inputBuildingBlock.name,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (!fs.existsSync(serviceFolderPath) && !dryRun) {
|
|
69
|
+
fs.mkdirSync(serviceFolderPath, { recursive: true });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const stringifiedOpenApi = JSON.stringify(openApiParsed, null, 2);
|
|
73
|
+
const storedDefinitionFilePath = getAbsolutePath(
|
|
74
|
+
serviceFolderPath,
|
|
75
|
+
OPEN_API_DEFINITION_FILE_NAME,
|
|
76
|
+
);
|
|
77
|
+
const oldFilePath = getAbsolutePath(
|
|
78
|
+
serviceFolderPath,
|
|
79
|
+
OLD_OPEN_API_DEFINITION_FILE_NAME,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
log(`${inputBuildingBlock.name} - Storing Open API definition file`);
|
|
83
|
+
let fileBackup: FileBackup = { newPath: null, oldPath: null };
|
|
84
|
+
if (!dryRun) {
|
|
85
|
+
fileBackup = storeFilesWithBackup({
|
|
86
|
+
oldPath: oldFilePath,
|
|
87
|
+
latestVersionContent: stringifiedOpenApi,
|
|
88
|
+
latestVersionPath: storedDefinitionFilePath,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
log(
|
|
92
|
+
`${inputBuildingBlock.name} - Open API definition file stored!`,
|
|
93
|
+
fileBackup,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
fileContent: stringifiedOpenApi,
|
|
98
|
+
filePath: storedDefinitionFilePath,
|
|
99
|
+
buildingBlockFolder: serviceFolderPath,
|
|
100
|
+
fileBackup,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function storeSchema({
|
|
105
|
+
openApiDefinitionContent,
|
|
106
|
+
buildingBlockFolder,
|
|
107
|
+
inputBuildingBlock,
|
|
108
|
+
dryRun,
|
|
109
|
+
}: {
|
|
110
|
+
openApiDefinitionContent: string;
|
|
111
|
+
buildingBlockFolder: string;
|
|
112
|
+
inputBuildingBlock: ConfigurationBuildingBlock;
|
|
113
|
+
dryRun: boolean;
|
|
114
|
+
}): Promise<FileBackup> {
|
|
115
|
+
log(`${inputBuildingBlock.name} - Creating TS Schema`);
|
|
116
|
+
const parser = await openapiTS(openApiDefinitionContent);
|
|
117
|
+
const parsedSchema = astToString(parser);
|
|
118
|
+
log(`${inputBuildingBlock.name} - TS Schema created`);
|
|
119
|
+
const schemaFilePath = getAbsolutePath(buildingBlockFolder, SCHEMA_FILE_NAME);
|
|
120
|
+
const oldPath = getAbsolutePath(buildingBlockFolder, OLD_SCHEMA_FILE_NAME);
|
|
121
|
+
|
|
122
|
+
log(`${inputBuildingBlock.name} - Storing TS Schema`);
|
|
123
|
+
let fileBackup: FileBackup = { newPath: null, oldPath: null };
|
|
124
|
+
if (!dryRun) {
|
|
125
|
+
fileBackup = storeFilesWithBackup({
|
|
126
|
+
oldPath,
|
|
127
|
+
latestVersionContent: parsedSchema,
|
|
128
|
+
latestVersionPath: schemaFilePath,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
log(`${inputBuildingBlock.name} - TS Schema stored`);
|
|
132
|
+
|
|
133
|
+
return fileBackup;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function deleteOldFiles(
|
|
137
|
+
buildingBlockName: string,
|
|
138
|
+
...backedUpFiles: FileBackup[]
|
|
139
|
+
): void {
|
|
140
|
+
for (const backedUp of backedUpFiles) {
|
|
141
|
+
try {
|
|
142
|
+
// if an old version has been backupped
|
|
143
|
+
// but all went fine, delete it
|
|
144
|
+
if (backedUp.oldPath) {
|
|
145
|
+
fs.unlinkSync(backedUp.oldPath);
|
|
146
|
+
}
|
|
147
|
+
} catch (e) {
|
|
148
|
+
log(`${buildingBlockName} - Error deleting ${backedUp.oldPath}`, e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function restoreOldFiles(
|
|
154
|
+
buildingBlockName: string,
|
|
155
|
+
...backedUpFiles: FileBackup[]
|
|
156
|
+
): void {
|
|
157
|
+
for (const backedUp of backedUpFiles) {
|
|
158
|
+
try {
|
|
159
|
+
if (backedUp.oldPath) {
|
|
160
|
+
// if a backup is available and the
|
|
161
|
+
// new file has been written
|
|
162
|
+
// revert it
|
|
163
|
+
if (backedUp.newPath) {
|
|
164
|
+
fs.copyFileSync(backedUp.oldPath, backedUp.newPath);
|
|
165
|
+
}
|
|
166
|
+
fs.unlinkSync(backedUp.oldPath);
|
|
167
|
+
} else if (backedUp.newPath) {
|
|
168
|
+
// if the new file has been saved
|
|
169
|
+
// and something went wrong
|
|
170
|
+
// delete it
|
|
171
|
+
fs.unlinkSync(backedUp.newPath);
|
|
172
|
+
}
|
|
173
|
+
} catch (e) {
|
|
174
|
+
log(`${buildingBlockName} - Error restoring ${backedUp.newPath}`, e);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function storeFilesWithBackup({
|
|
180
|
+
oldPath,
|
|
181
|
+
latestVersionPath,
|
|
182
|
+
latestVersionContent,
|
|
183
|
+
}: {
|
|
184
|
+
oldPath: string;
|
|
185
|
+
latestVersionPath: string;
|
|
186
|
+
latestVersionContent: string;
|
|
187
|
+
}): FileBackup {
|
|
188
|
+
const output: FileBackup = { newPath: null, oldPath: null };
|
|
189
|
+
// if the file already exists
|
|
190
|
+
// backup it
|
|
191
|
+
if (fs.existsSync(latestVersionPath)) {
|
|
192
|
+
output.oldPath = oldPath;
|
|
193
|
+
fs.copyFileSync(latestVersionPath, oldPath);
|
|
194
|
+
}
|
|
195
|
+
// write the new file
|
|
196
|
+
fs.writeFileSync(latestVersionPath, latestVersionContent);
|
|
197
|
+
output.newPath = latestVersionPath;
|
|
198
|
+
|
|
199
|
+
return output;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function processBuildingBlock({
|
|
203
|
+
inputBuildingBlock,
|
|
204
|
+
dryRun,
|
|
205
|
+
}: {
|
|
206
|
+
inputBuildingBlock: ConfigurationBuildingBlock;
|
|
207
|
+
dryRun: boolean;
|
|
208
|
+
}): Promise<void> {
|
|
209
|
+
let schemaFileBackup: FileBackup = {
|
|
210
|
+
oldPath: null,
|
|
211
|
+
newPath: null,
|
|
212
|
+
};
|
|
213
|
+
let storedDefinition = null;
|
|
214
|
+
if (inputBuildingBlock.updateDefinitions === false) {
|
|
215
|
+
log(`${inputBuildingBlock.name} - Update definition is disabled, ignoring`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
log(`${inputBuildingBlock.name} - Processing`);
|
|
219
|
+
try {
|
|
220
|
+
storedDefinition = await storeOpenApiDefinitionFile({
|
|
221
|
+
inputBuildingBlock,
|
|
222
|
+
dryRun,
|
|
223
|
+
});
|
|
224
|
+
schemaFileBackup = await storeSchema({
|
|
225
|
+
inputBuildingBlock,
|
|
226
|
+
openApiDefinitionContent: storedDefinition.fileContent,
|
|
227
|
+
buildingBlockFolder: storedDefinition.buildingBlockFolder,
|
|
228
|
+
dryRun,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
log(`${inputBuildingBlock.name} - Deleting backup files`);
|
|
232
|
+
if (!dryRun) {
|
|
233
|
+
deleteOldFiles(
|
|
234
|
+
inputBuildingBlock.name,
|
|
235
|
+
storedDefinition.fileBackup,
|
|
236
|
+
schemaFileBackup,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
} catch (e) {
|
|
240
|
+
log(
|
|
241
|
+
`${inputBuildingBlock.name} - Error While Processing, restoring backups`,
|
|
242
|
+
);
|
|
243
|
+
if (!dryRun) {
|
|
244
|
+
restoreOldFiles(
|
|
245
|
+
inputBuildingBlock.name,
|
|
246
|
+
storedDefinition?.fileBackup ?? { newPath: null, oldPath: null },
|
|
247
|
+
schemaFileBackup,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
throw e;
|
|
252
|
+
}
|
|
253
|
+
log(`${inputBuildingBlock.name} - Processed`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function updateClients({
|
|
257
|
+
configurationFilePath,
|
|
258
|
+
dryRun,
|
|
259
|
+
}: { configurationFilePath: string; dryRun?: boolean }): Promise<void> {
|
|
260
|
+
dryRun = dryRun ?? false;
|
|
261
|
+
const configurationFile = await readConfigurationFile(configurationFilePath);
|
|
262
|
+
log("Configuration file read");
|
|
263
|
+
|
|
264
|
+
const promises: Promise<void>[] = [];
|
|
265
|
+
for (const inputBuilding of Object.values(configurationFile.buildingBlocks)) {
|
|
266
|
+
promises.push(
|
|
267
|
+
processBuildingBlock({ inputBuildingBlock: inputBuilding, dryRun }),
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
await Promise.all(promises);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const updateClientsCommand = new Command("clients:update");
|
|
275
|
+
updateClientsCommand
|
|
276
|
+
.description(
|
|
277
|
+
"Parse a configuration file to update the info related to the clients",
|
|
278
|
+
)
|
|
279
|
+
.requiredOption(
|
|
280
|
+
"-c, --configuration-file-path <configuration-path>",
|
|
281
|
+
"Path of the configuration file to parse",
|
|
282
|
+
(value: string) => getAbsolutePath(value),
|
|
283
|
+
)
|
|
284
|
+
.option("--dry-run", "Simulate the command without executing it")
|
|
285
|
+
.action(
|
|
286
|
+
async (options: { configurationFilePath: string; dryRun?: boolean }) => {
|
|
287
|
+
log("Started!");
|
|
288
|
+
if (options.dryRun) {
|
|
289
|
+
log(
|
|
290
|
+
"Alert. I am running dry! Changes will be logged only, not applied!",
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
await updateClients(options);
|
|
294
|
+
log("Ended!");
|
|
295
|
+
},
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
export default updateClientsCommand;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import configFile from "../../clients-configurations/clients-configuration.json";
|
|
2
|
+
import type {
|
|
3
|
+
GetAccessTokenParams,
|
|
4
|
+
GetOrganizationTokenParams,
|
|
5
|
+
M2MTokenFnConfig,
|
|
6
|
+
SERVICE_NAME,
|
|
7
|
+
TokenFunction,
|
|
8
|
+
TokenResponseBody,
|
|
9
|
+
} from "../../types/index.js";
|
|
10
|
+
|
|
11
|
+
const fetchToken = async (params: {
|
|
12
|
+
logtoOidcEndpoint: string;
|
|
13
|
+
applicationId: string;
|
|
14
|
+
applicationSecret: string;
|
|
15
|
+
scopes: string[];
|
|
16
|
+
specificBodyFields: { [x: string]: string };
|
|
17
|
+
}) => {
|
|
18
|
+
const body = {
|
|
19
|
+
...params.specificBodyFields,
|
|
20
|
+
scope: params.scopes.join(" "),
|
|
21
|
+
grant_type: "client_credentials",
|
|
22
|
+
};
|
|
23
|
+
const logtoOidcEndpoint = params.logtoOidcEndpoint.endsWith("/")
|
|
24
|
+
? params.logtoOidcEndpoint
|
|
25
|
+
: `${params.logtoOidcEndpoint}/`;
|
|
26
|
+
const response = await fetch(`${logtoOidcEndpoint}token`, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
30
|
+
Authorization: `Basic ${Buffer.from(
|
|
31
|
+
`${params.applicationId}:${params.applicationSecret}`,
|
|
32
|
+
).toString("base64")}`,
|
|
33
|
+
},
|
|
34
|
+
body: new URLSearchParams(body).toString(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (response.status !== 200) {
|
|
38
|
+
throw new Error("Unauthorized");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return response.json() as Promise<TokenResponseBody>;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const getAccessToken = async (
|
|
45
|
+
params: GetAccessTokenParams,
|
|
46
|
+
scopes: string[],
|
|
47
|
+
) => {
|
|
48
|
+
const scopesToUse = params.scopes || scopes;
|
|
49
|
+
|
|
50
|
+
const tokenResponse = await fetchToken({
|
|
51
|
+
...params,
|
|
52
|
+
scopes: scopesToUse,
|
|
53
|
+
specificBodyFields: { resource: params.resource },
|
|
54
|
+
});
|
|
55
|
+
return tokenResponse.access_token;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const getOrganizationToken = async (
|
|
59
|
+
params: GetOrganizationTokenParams,
|
|
60
|
+
scopes: string[],
|
|
61
|
+
) => {
|
|
62
|
+
const logtoUserScopes = await importUserScopes();
|
|
63
|
+
|
|
64
|
+
const { UserScope } = logtoUserScopes;
|
|
65
|
+
|
|
66
|
+
const scopesToUse = params.scopes || scopes;
|
|
67
|
+
|
|
68
|
+
const tokenResponse = await fetchToken({
|
|
69
|
+
...params,
|
|
70
|
+
scopes: [
|
|
71
|
+
...scopesToUse,
|
|
72
|
+
UserScope.OrganizationRoles,
|
|
73
|
+
UserScope.Organizations,
|
|
74
|
+
],
|
|
75
|
+
specificBodyFields: {
|
|
76
|
+
organization_id: params.organizationId,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
return tokenResponse.access_token;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const importUserScopes = async () => {
|
|
83
|
+
try {
|
|
84
|
+
const { UserScope } = await import("@logto/node");
|
|
85
|
+
|
|
86
|
+
return { UserScope };
|
|
87
|
+
} catch {
|
|
88
|
+
throw new Error("@logto/node package is not installed!");
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getM2MTokenFn = (m2mTokenConfig: M2MTokenFnConfig): TokenFunction => {
|
|
93
|
+
const { services } = m2mTokenConfig;
|
|
94
|
+
|
|
95
|
+
const tokenFn = (serviceName: SERVICE_NAME) => {
|
|
96
|
+
const serviceParams = services[serviceName];
|
|
97
|
+
|
|
98
|
+
// const scopes = configFile.buildingBlocks[serviceName];
|
|
99
|
+
|
|
100
|
+
const serviceConfig = configFile.buildingBlocks.find(
|
|
101
|
+
({ name }) => name === serviceName,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (!serviceConfig) {
|
|
105
|
+
throw new Error(`Missing client config for ${serviceName}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!serviceParams) {
|
|
109
|
+
throw new Error(`Missing m2m config for ${serviceName}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const { getAccessTokenParams, getOrganizationTokenParams } = serviceParams;
|
|
113
|
+
if (getAccessTokenParams) {
|
|
114
|
+
return getAccessToken(
|
|
115
|
+
getAccessTokenParams,
|
|
116
|
+
serviceConfig.citizenPermissions,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
if (getOrganizationTokenParams) {
|
|
120
|
+
return getOrganizationToken(
|
|
121
|
+
getOrganizationTokenParams,
|
|
122
|
+
serviceConfig.publicServantPermissions,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
throw new Error(`wrong m2m config for ${serviceName}`);
|
|
127
|
+
};
|
|
128
|
+
return tokenFn;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export default getM2MTokenFn;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import createClient, {
|
|
2
|
+
type Middleware,
|
|
3
|
+
type FetchResponse,
|
|
4
|
+
} from "openapi-fetch";
|
|
5
|
+
import type {
|
|
6
|
+
ApiClientParams,
|
|
7
|
+
SERVICE_NAME,
|
|
8
|
+
TokenFunction,
|
|
9
|
+
} from "../types/index.js";
|
|
10
|
+
import type { DataResponseValue } from "./utils/client-utils.js";
|
|
11
|
+
|
|
12
|
+
abstract class BaseClient<T extends {}> {
|
|
13
|
+
private baseUrl?: string;
|
|
14
|
+
private initialized;
|
|
15
|
+
|
|
16
|
+
protected token?: string;
|
|
17
|
+
protected getTokenFn?: TokenFunction;
|
|
18
|
+
protected serviceName: SERVICE_NAME | undefined;
|
|
19
|
+
|
|
20
|
+
protected client: ReturnType<typeof createClient<T>>;
|
|
21
|
+
|
|
22
|
+
constructor({ baseUrl, getTokenFn }: ApiClientParams) {
|
|
23
|
+
this.initialized = false;
|
|
24
|
+
if (baseUrl) {
|
|
25
|
+
this.baseUrl = baseUrl;
|
|
26
|
+
this.initialized = true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (getTokenFn) {
|
|
30
|
+
this.getTokenFn = getTokenFn;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.token = undefined;
|
|
34
|
+
this.client = createClient<T>({ baseUrl: this.baseUrl });
|
|
35
|
+
const authMiddleware: Middleware = {
|
|
36
|
+
onRequest: async ({ request }) => {
|
|
37
|
+
if (!this.token && this.getTokenFn) {
|
|
38
|
+
this.token = await this.getTokenFn(this.serviceName as SERVICE_NAME);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (this.token) {
|
|
42
|
+
request.headers.set("Authorization", `Bearer ${this.token}`);
|
|
43
|
+
}
|
|
44
|
+
return request;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
this.client.use(authMiddleware);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected async getToken() {
|
|
52
|
+
if (this.getTokenFn) {
|
|
53
|
+
const token = await this.getTokenFn(this.serviceName as SERVICE_NAME);
|
|
54
|
+
this.token = token;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return this.token;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public isInitialized() {
|
|
61
|
+
return this.initialized;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected formatResponse<G, O>(
|
|
65
|
+
response: FetchResponse<G, O, "application/json">,
|
|
66
|
+
): DataResponseValue<G, O> {
|
|
67
|
+
let outputData = undefined;
|
|
68
|
+
let outputMetadata = undefined;
|
|
69
|
+
if (!response) {
|
|
70
|
+
return {} as DataResponseValue<G, O>;
|
|
71
|
+
}
|
|
72
|
+
if (response.data) {
|
|
73
|
+
const dataEntries = Object.entries(response.data);
|
|
74
|
+
// by docs the body should contain a "data"
|
|
75
|
+
// properties with the response values
|
|
76
|
+
const containsData = dataEntries.find((x) => x[0] === "data");
|
|
77
|
+
const containsMetadata = dataEntries.find((x) => x[0] === "metadata");
|
|
78
|
+
|
|
79
|
+
if (containsMetadata) {
|
|
80
|
+
outputMetadata = containsMetadata[1];
|
|
81
|
+
}
|
|
82
|
+
outputData = containsData ? containsData[1] : response.data;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
data: outputData,
|
|
87
|
+
metadata: outputMetadata,
|
|
88
|
+
error: response.error,
|
|
89
|
+
} as unknown as DataResponseValue<G, O>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
protected formatError<G, O>(reason: unknown): DataResponseValue<G, O> {
|
|
93
|
+
return { error: reason } as unknown as DataResponseValue<G, O>;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default BaseClient;
|