@twin.org/api-tenant-processor 0.0.3-next.4 → 0.0.3-next.41
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/README.md +1 -1
- package/dist/es/entities/tenant.js +17 -1
- package/dist/es/entities/tenant.js.map +1 -1
- package/dist/es/index.js +13 -2
- package/dist/es/index.js.map +1 -1
- package/dist/es/models/ITenantProcessorConfig.js.map +1 -1
- package/dist/es/models/ITenantProcessorConstructorOptions.js.map +1 -1
- package/dist/es/models/ITenantServiceConfig.js +4 -0
- package/dist/es/models/ITenantServiceConfig.js.map +1 -0
- package/dist/es/models/ITenantServiceConstructorOptions.js +2 -0
- package/dist/es/models/ITenantServiceConstructorOptions.js.map +1 -0
- package/dist/es/models/api/ITenantCreateRequest.js +2 -0
- package/dist/es/models/api/ITenantCreateRequest.js.map +1 -0
- package/dist/es/models/api/ITenantGetByApiKeyRequest.js +4 -0
- package/dist/es/models/api/ITenantGetByApiKeyRequest.js.map +1 -0
- package/dist/es/models/api/ITenantGetByIdRequest.js +4 -0
- package/dist/es/models/api/ITenantGetByIdRequest.js.map +1 -0
- package/dist/es/models/api/ITenantGetByPublicOriginRequest.js +4 -0
- package/dist/es/models/api/ITenantGetByPublicOriginRequest.js.map +1 -0
- package/dist/es/models/api/ITenantGetResponse.js +2 -0
- package/dist/es/models/api/ITenantGetResponse.js.map +1 -0
- package/dist/es/models/api/ITenantListRequest.js +4 -0
- package/dist/es/models/api/ITenantListRequest.js.map +1 -0
- package/dist/es/models/api/ITenantListResponse.js +2 -0
- package/dist/es/models/api/ITenantListResponse.js.map +1 -0
- package/dist/es/models/api/ITenantRemoveRequest.js +4 -0
- package/dist/es/models/api/ITenantRemoveRequest.js.map +1 -0
- package/dist/es/models/api/ITenantUpdateRequest.js +2 -0
- package/dist/es/models/api/ITenantUpdateRequest.js.map +1 -0
- package/dist/es/restEntryPoints.js +10 -0
- package/dist/es/restEntryPoints.js.map +1 -0
- package/dist/es/tenantAdminService.js +101 -16
- package/dist/es/tenantAdminService.js.map +1 -1
- package/dist/es/tenantIdContextIdHandler.js +7 -0
- package/dist/es/tenantIdContextIdHandler.js.map +1 -1
- package/dist/es/tenantProcessor.js +75 -17
- package/dist/es/tenantProcessor.js.map +1 -1
- package/dist/es/tenantRoutes.js +394 -0
- package/dist/es/tenantRoutes.js.map +1 -0
- package/dist/es/tenantService.js +51 -0
- package/dist/es/tenantService.js.map +1 -0
- package/dist/es/utils/tenantIdHelper.js +3 -3
- package/dist/es/utils/tenantIdHelper.js.map +1 -1
- package/dist/types/entities/tenant.d.ts +8 -0
- package/dist/types/index.d.ts +13 -2
- package/dist/types/models/ITenantProcessorConfig.d.ts +4 -0
- package/dist/types/models/ITenantProcessorConstructorOptions.d.ts +5 -0
- package/dist/types/models/ITenantServiceConfig.d.ts +5 -0
- package/dist/types/models/ITenantServiceConstructorOptions.d.ts +15 -0
- package/dist/types/models/api/ITenantCreateRequest.d.ts +12 -0
- package/dist/types/models/api/ITenantGetByApiKeyRequest.d.ts +14 -0
- package/dist/types/models/api/ITenantGetByIdRequest.d.ts +14 -0
- package/dist/types/models/api/ITenantGetByPublicOriginRequest.d.ts +14 -0
- package/dist/types/models/api/ITenantGetResponse.d.ts +10 -0
- package/dist/types/models/api/ITenantListRequest.d.ts +23 -0
- package/dist/types/models/api/ITenantListResponse.d.ts +17 -0
- package/dist/types/models/api/ITenantRemoveRequest.d.ts +14 -0
- package/dist/types/models/api/ITenantUpdateRequest.d.ts +19 -0
- package/dist/types/restEntryPoints.d.ts +2 -0
- package/dist/types/tenantAdminService.d.ts +28 -11
- package/dist/types/tenantIdContextIdHandler.d.ts +5 -0
- package/dist/types/tenantProcessor.d.ts +1 -1
- package/dist/types/tenantRoutes.d.ts +77 -0
- package/dist/types/tenantService.d.ts +27 -0
- package/docs/changelog.md +546 -15
- package/docs/examples.md +81 -1
- package/docs/reference/classes/Tenant.md +20 -4
- package/docs/reference/classes/TenantAdminService.md +96 -26
- package/docs/reference/classes/TenantIdContextIdHandler.md +21 -3
- package/docs/reference/classes/TenantIdHelper.md +2 -2
- package/docs/reference/classes/TenantProcessor.md +6 -6
- package/docs/reference/classes/TenantService.md +79 -0
- package/docs/reference/functions/generateRestRoutesTenants.md +25 -0
- package/docs/reference/functions/tenantByApiKey.md +31 -0
- package/docs/reference/functions/tenantById.md +31 -0
- package/docs/reference/functions/tenantByPublicOrigin.md +31 -0
- package/docs/reference/functions/tenantCreate.md +31 -0
- package/docs/reference/functions/tenantList.md +31 -0
- package/docs/reference/functions/tenantRemove.md +31 -0
- package/docs/reference/functions/tenantUpdate.md +31 -0
- package/docs/reference/index.md +24 -2
- package/docs/reference/interfaces/ITenantAdminServiceConstructorOptions.md +4 -4
- package/docs/reference/interfaces/ITenantCreateRequest.md +17 -0
- package/docs/reference/interfaces/ITenantGetByApiKeyRequest.md +17 -0
- package/docs/reference/interfaces/ITenantGetByIdRequest.md +17 -0
- package/docs/reference/interfaces/ITenantGetByPublicOriginRequest.md +17 -0
- package/docs/reference/interfaces/ITenantGetResponse.md +11 -0
- package/docs/reference/interfaces/ITenantListRequest.md +30 -0
- package/docs/reference/interfaces/ITenantListResponse.md +23 -0
- package/docs/reference/interfaces/ITenantProcessorConfig.md +10 -2
- package/docs/reference/interfaces/ITenantProcessorConstructorOptions.md +18 -4
- package/docs/reference/interfaces/ITenantRemoveRequest.md +17 -0
- package/docs/reference/interfaces/ITenantServiceConfig.md +3 -0
- package/docs/reference/interfaces/ITenantServiceConstructorOptions.md +25 -0
- package/docs/reference/interfaces/ITenantUpdateRequest.md +25 -0
- package/docs/reference/variables/tagsTenants.md +5 -0
- package/locales/en.json +8 -2
- package/package.json +6 -5
- package/dist/es/models/ITenant.js +0 -4
- package/dist/es/models/ITenant.js.map +0 -1
- package/dist/es/models/ITenantAdminComponent.js +0 -2
- package/dist/es/models/ITenantAdminComponent.js.map +0 -1
- package/dist/types/models/ITenant.d.ts +0 -21
- package/dist/types/models/ITenantAdminComponent.d.ts +0 -41
- package/docs/reference/interfaces/ITenant.md +0 -35
- package/docs/reference/interfaces/ITenantAdminComponent.md +0 -123
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
3
|
import { HttpErrorHelper } from "@twin.org/api-models";
|
|
4
4
|
import { ContextIdKeys } from "@twin.org/context";
|
|
5
|
-
import { BaseError, Is, UnauthorizedError } from "@twin.org/core";
|
|
5
|
+
import { BaseError, ComponentFactory, Is, UnauthorizedError } from "@twin.org/core";
|
|
6
6
|
import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
|
|
7
7
|
import { HttpStatusCode } from "@twin.org/web";
|
|
8
8
|
/**
|
|
@@ -18,23 +18,40 @@ export class TenantProcessor {
|
|
|
18
18
|
* Runtime name for the class.
|
|
19
19
|
*/
|
|
20
20
|
static CLASS_NAME = "TenantProcessor";
|
|
21
|
+
/**
|
|
22
|
+
* The default endpoints to match for api key processing.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
static _DEFAULT_API_KEY_ENDPOINTS = ["/login$"];
|
|
21
26
|
/**
|
|
22
27
|
* The entity storage for api keys.
|
|
23
28
|
* @internal
|
|
24
29
|
*/
|
|
25
30
|
_entityStorageConnector;
|
|
31
|
+
/**
|
|
32
|
+
* The transformer component, used to resolve public origins for tenants and encrypt/decrypt tenant tokens.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
_urlTransformerService;
|
|
26
36
|
/**
|
|
27
37
|
* The key in the header to look for the api key.
|
|
28
38
|
* @internal
|
|
29
39
|
*/
|
|
30
40
|
_apiKeyName;
|
|
31
41
|
/**
|
|
32
|
-
*
|
|
42
|
+
* The list of regexp patterns to match against the request URL to determine if the api key header should be processed.
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
_apiKeyEndpoints;
|
|
46
|
+
/**
|
|
47
|
+
* Create a new instance of TenantProcessor.
|
|
33
48
|
* @param options Options for the processor.
|
|
34
49
|
*/
|
|
35
50
|
constructor(options) {
|
|
36
51
|
this._entityStorageConnector = EntityStorageConnectorFactory.get(options?.tenantEntityStorageType ?? "tenant");
|
|
52
|
+
this._urlTransformerService = ComponentFactory.get(options?.urlTransformerComponentType ?? "url-transformer");
|
|
37
53
|
this._apiKeyName = options?.config?.apiKeyName ?? TenantProcessor.DEFAULT_API_KEY_NAME;
|
|
54
|
+
this._apiKeyEndpoints = (options?.config?.apiKeyEndpoints ?? TenantProcessor._DEFAULT_API_KEY_ENDPOINTS).map((ep) => new RegExp(ep));
|
|
38
55
|
}
|
|
39
56
|
/**
|
|
40
57
|
* Returns the class name of the component.
|
|
@@ -52,32 +69,73 @@ export class TenantProcessor {
|
|
|
52
69
|
* @param processorState The state handed through the processors.
|
|
53
70
|
*/
|
|
54
71
|
async pre(request, response, route, contextIds, processorState) {
|
|
55
|
-
|
|
56
|
-
let errorResponse;
|
|
57
|
-
if (Is.stringValue(apiKey)) {
|
|
72
|
+
if (!Is.empty(route) && !(route.skipTenant ?? false)) {
|
|
58
73
|
try {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
let tenant;
|
|
75
|
+
const isSkipAuth = route.skipAuth ?? false;
|
|
76
|
+
const urlPath = new URL(request.url, "http://localhost").pathname;
|
|
77
|
+
const isApiKeyEndpoint = this._apiKeyEndpoints.some(re => re.test(urlPath));
|
|
78
|
+
if (isApiKeyEndpoint) {
|
|
79
|
+
const apiKey = request.headers?.[this._apiKeyName] ?? request.query?.[this._apiKeyName];
|
|
80
|
+
if (Is.stringValue(apiKey)) {
|
|
81
|
+
tenant = await this.resolveByApiKey(apiKey);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new UnauthorizedError(TenantProcessor.CLASS_NAME, "missingApiKey", {
|
|
85
|
+
keyName: this._apiKeyName
|
|
86
|
+
});
|
|
87
|
+
}
|
|
64
88
|
}
|
|
65
89
|
else {
|
|
66
|
-
|
|
90
|
+
const tenantToken = await this._urlTransformerService.getEncryptedQueryParam(request.query, "tenant");
|
|
91
|
+
if (Is.stringValue(tenantToken)) {
|
|
92
|
+
tenant = await this.resolveByTenantToken(tenantToken);
|
|
93
|
+
}
|
|
94
|
+
else if (isSkipAuth) {
|
|
95
|
+
throw new UnauthorizedError(TenantProcessor.CLASS_NAME, "missingTenantToken", {
|
|
96
|
+
paramName: this._urlTransformerService.getParamName("tenant")
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!Is.empty(tenant)) {
|
|
101
|
+
contextIds[ContextIdKeys.Tenant] = tenant.id;
|
|
102
|
+
processorState.publicOrigin = tenant.publicOrigin;
|
|
67
103
|
}
|
|
68
104
|
}
|
|
69
105
|
catch (err) {
|
|
70
|
-
|
|
106
|
+
HttpErrorHelper.buildResponse(response, BaseError.fromError(err), HttpStatusCode.unauthorized);
|
|
71
107
|
}
|
|
72
108
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Resolve the tenant context from an api key.
|
|
112
|
+
* @param apiKey The api key sent by the caller.
|
|
113
|
+
* @returns The tenant associated with the api key.
|
|
114
|
+
* @internal
|
|
115
|
+
*/
|
|
116
|
+
async resolveByApiKey(apiKey) {
|
|
117
|
+
const nodeTenant = await this._entityStorageConnector.get(apiKey, "apiKey");
|
|
118
|
+
if (Is.empty(nodeTenant)) {
|
|
119
|
+
throw new UnauthorizedError(TenantProcessor.CLASS_NAME, "apiKeyNotFound", {
|
|
120
|
+
key: apiKey
|
|
76
121
|
});
|
|
77
122
|
}
|
|
78
|
-
|
|
79
|
-
|
|
123
|
+
return nodeTenant;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Resolve the tenant context from an encrypted tenant token query param.
|
|
127
|
+
* @param tenantId The encrypted tenant token.
|
|
128
|
+
* @returns The tenant associated with the tenant token.
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
131
|
+
async resolveByTenantToken(tenantId) {
|
|
132
|
+
const nodeTenant = await this._entityStorageConnector.get(tenantId);
|
|
133
|
+
if (Is.empty(nodeTenant)) {
|
|
134
|
+
throw new UnauthorizedError(TenantProcessor.CLASS_NAME, "tenantNotFound", {
|
|
135
|
+
tenantId
|
|
136
|
+
});
|
|
80
137
|
}
|
|
138
|
+
return nodeTenant;
|
|
81
139
|
}
|
|
82
140
|
}
|
|
83
141
|
//# sourceMappingURL=tenantProcessor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenantProcessor.js","sourceRoot":"","sources":["../../src/tenantProcessor.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EACN,eAAe,
|
|
1
|
+
{"version":3,"file":"tenantProcessor.js","sourceRoot":"","sources":["../../src/tenantProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpErrorHelper,\n\ttype IBaseRoute,\n\ttype IBaseRouteProcessor,\n\ttype IHttpResponse,\n\ttype IHttpServerRequest,\n\ttype IUrlTransformerComponent\n} from \"@twin.org/api-models\";\nimport { ContextIdKeys, type IContextIds } from \"@twin.org/context\";\nimport { BaseError, ComponentFactory, Is, UnauthorizedError } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HttpStatusCode } from \"@twin.org/web\";\nimport type { Tenant } from \"./entities/tenant.js\";\nimport type { ITenantProcessorConstructorOptions } from \"./models/ITenantProcessorConstructorOptions.js\";\n\n/**\n * Handles incoming api keys and maps them to tenant ids.\n */\nexport class TenantProcessor implements IBaseRouteProcessor {\n\t/**\n\t * The default name for the api key header.\n\t * @internal\n\t */\n\tpublic static readonly DEFAULT_API_KEY_NAME: string = \"x-api-key\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TenantProcessor>();\n\n\t/**\n\t * The default endpoints to match for api key processing.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_API_KEY_ENDPOINTS: string[] = [\"/login$\"];\n\n\t/**\n\t * The entity storage for api keys.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnector: IEntityStorageConnector<Tenant>;\n\n\t/**\n\t * The transformer component, used to resolve public origins for tenants and encrypt/decrypt tenant tokens.\n\t * @internal\n\t */\n\tprivate readonly _urlTransformerService: IUrlTransformerComponent;\n\n\t/**\n\t * The key in the header to look for the api key.\n\t * @internal\n\t */\n\tprivate readonly _apiKeyName: string;\n\n\t/**\n\t * The list of regexp patterns to match against the request URL to determine if the api key header should be processed.\n\t * @internal\n\t */\n\tprivate readonly _apiKeyEndpoints: RegExp[];\n\n\t/**\n\t * Create a new instance of TenantProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: ITenantProcessorConstructorOptions) {\n\t\tthis._entityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\toptions?.tenantEntityStorageType ?? \"tenant\"\n\t\t);\n\t\tthis._urlTransformerService = ComponentFactory.get(\n\t\t\toptions?.urlTransformerComponentType ?? \"url-transformer\"\n\t\t);\n\t\tthis._apiKeyName = options?.config?.apiKeyName ?? TenantProcessor.DEFAULT_API_KEY_NAME;\n\t\tthis._apiKeyEndpoints = (\n\t\t\toptions?.config?.apiKeyEndpoints ?? TenantProcessor._DEFAULT_API_KEY_ENDPOINTS\n\t\t).map((ep: string) => new RegExp(ep));\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn TenantProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * Pre process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t */\n\tpublic async pre(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tif (!Is.empty(route) && !(route.skipTenant ?? false)) {\n\t\t\ttry {\n\t\t\t\tlet tenant;\n\t\t\t\tconst isSkipAuth = route.skipAuth ?? false;\n\t\t\t\tconst urlPath = new URL(request.url, \"http://localhost\").pathname;\n\t\t\t\tconst isApiKeyEndpoint = this._apiKeyEndpoints.some(re => re.test(urlPath));\n\n\t\t\t\tif (isApiKeyEndpoint) {\n\t\t\t\t\tconst apiKey = request.headers?.[this._apiKeyName] ?? request.query?.[this._apiKeyName];\n\t\t\t\t\tif (Is.stringValue(apiKey)) {\n\t\t\t\t\t\ttenant = await this.resolveByApiKey(apiKey);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UnauthorizedError(TenantProcessor.CLASS_NAME, \"missingApiKey\", {\n\t\t\t\t\t\t\tkeyName: this._apiKeyName\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst tenantToken = await this._urlTransformerService.getEncryptedQueryParam(\n\t\t\t\t\t\trequest.query,\n\t\t\t\t\t\t\"tenant\"\n\t\t\t\t\t);\n\t\t\t\t\tif (Is.stringValue(tenantToken)) {\n\t\t\t\t\t\ttenant = await this.resolveByTenantToken(tenantToken);\n\t\t\t\t\t} else if (isSkipAuth) {\n\t\t\t\t\t\tthrow new UnauthorizedError(TenantProcessor.CLASS_NAME, \"missingTenantToken\", {\n\t\t\t\t\t\t\tparamName: this._urlTransformerService.getParamName(\"tenant\")\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!Is.empty(tenant)) {\n\t\t\t\t\tcontextIds[ContextIdKeys.Tenant] = tenant.id;\n\t\t\t\t\tprocessorState.publicOrigin = tenant.publicOrigin;\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tHttpErrorHelper.buildResponse(\n\t\t\t\t\tresponse,\n\t\t\t\t\tBaseError.fromError(err),\n\t\t\t\t\tHttpStatusCode.unauthorized\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the tenant context from an api key.\n\t * @param apiKey The api key sent by the caller.\n\t * @returns The tenant associated with the api key.\n\t * @internal\n\t */\n\tprivate async resolveByApiKey(apiKey: string): Promise<Tenant> {\n\t\tconst nodeTenant = await this._entityStorageConnector.get(apiKey, \"apiKey\");\n\n\t\tif (Is.empty(nodeTenant)) {\n\t\t\tthrow new UnauthorizedError(TenantProcessor.CLASS_NAME, \"apiKeyNotFound\", {\n\t\t\t\tkey: apiKey\n\t\t\t});\n\t\t}\n\n\t\treturn nodeTenant;\n\t}\n\n\t/**\n\t * Resolve the tenant context from an encrypted tenant token query param.\n\t * @param tenantId The encrypted tenant token.\n\t * @returns The tenant associated with the tenant token.\n\t * @internal\n\t */\n\tprivate async resolveByTenantToken(tenantId: string): Promise<Tenant> {\n\t\tconst nodeTenant = await this._entityStorageConnector.get(tenantId);\n\n\t\tif (Is.empty(nodeTenant)) {\n\t\t\tthrow new UnauthorizedError(TenantProcessor.CLASS_NAME, \"tenantNotFound\", {\n\t\t\t\ttenantId\n\t\t\t});\n\t\t}\n\n\t\treturn nodeTenant;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { HttpParameterHelper } from "@twin.org/api-models";
|
|
4
|
+
import { Coerce, ComponentFactory, Guards, Is } from "@twin.org/core";
|
|
5
|
+
import { HeaderHelper, HeaderTypes, HttpStatusCode } from "@twin.org/web";
|
|
6
|
+
/**
|
|
7
|
+
* The source used when communicating about these routes.
|
|
8
|
+
*/
|
|
9
|
+
const ROUTES_SOURCE = "tenantRoutes";
|
|
10
|
+
/**
|
|
11
|
+
* The tag to associate with the routes.
|
|
12
|
+
*/
|
|
13
|
+
export const tagsTenants = [
|
|
14
|
+
{
|
|
15
|
+
name: "Tenants",
|
|
16
|
+
description: "Tenants endpoints for the REST server."
|
|
17
|
+
}
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* The REST routes for tenant management.
|
|
21
|
+
* @param baseRouteName Prefix to prepend to the paths.
|
|
22
|
+
* @param componentName The name of the component to use in the routes stored in the ComponentFactory.
|
|
23
|
+
* @returns The generated routes.
|
|
24
|
+
*/
|
|
25
|
+
export function generateRestRoutesTenants(baseRouteName, componentName) {
|
|
26
|
+
const tenantListRoute = {
|
|
27
|
+
operationId: "tenantList",
|
|
28
|
+
summary: "Get the list of tenants",
|
|
29
|
+
tag: tagsTenants[0].name,
|
|
30
|
+
method: "GET",
|
|
31
|
+
path: `${baseRouteName}/`,
|
|
32
|
+
handler: async (httpRequestContext, request) => tenantList(httpRequestContext, componentName, request),
|
|
33
|
+
responseType: [
|
|
34
|
+
{
|
|
35
|
+
type: "ITenantListResponse",
|
|
36
|
+
examples: [
|
|
37
|
+
{
|
|
38
|
+
id: "tenantListResponse",
|
|
39
|
+
description: "The response for the list tenants request.",
|
|
40
|
+
response: {
|
|
41
|
+
headers: {
|
|
42
|
+
[HeaderTypes.Link]: '<https://api.twin.org/tenants/1>; rel="next"'
|
|
43
|
+
},
|
|
44
|
+
body: [
|
|
45
|
+
{
|
|
46
|
+
id: "2a39d6e62d98aa5372432f2e5d2208d4",
|
|
47
|
+
apiKey: "ad7a5b0b816ca314b69c813ae1368232",
|
|
48
|
+
label: "node",
|
|
49
|
+
dateCreated: "2026-01-19T03:59:35.742Z",
|
|
50
|
+
dateModified: "2026-01-19T03:59:35.742Z",
|
|
51
|
+
publicOrigin: "https://example.com:4321"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
requiredScope: ["tenant-admin"]
|
|
60
|
+
};
|
|
61
|
+
const tenantGetByIdRoute = {
|
|
62
|
+
operationId: "tenantGetById",
|
|
63
|
+
summary: "Get the tenant by id",
|
|
64
|
+
tag: tagsTenants[0].name,
|
|
65
|
+
method: "GET",
|
|
66
|
+
path: `${baseRouteName}/:id`,
|
|
67
|
+
handler: async (httpRequestContext, request) => tenantById(httpRequestContext, componentName, request),
|
|
68
|
+
requestType: {
|
|
69
|
+
type: "ITenantGetByIdRequest",
|
|
70
|
+
examples: [
|
|
71
|
+
{
|
|
72
|
+
id: "tenantGetByIdRequestExample",
|
|
73
|
+
request: {
|
|
74
|
+
pathParams: {
|
|
75
|
+
id: "2a39d6e62d98aa5372432f2e5d2208d4"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
responseType: [
|
|
82
|
+
{
|
|
83
|
+
type: "ITenantGetResponse",
|
|
84
|
+
examples: [
|
|
85
|
+
{
|
|
86
|
+
id: "tenantGetByIdResponse",
|
|
87
|
+
description: "The response for the get tenant by id request.",
|
|
88
|
+
response: {
|
|
89
|
+
body: {
|
|
90
|
+
id: "2a39d6e62d98aa5372432f2e5d2208d4",
|
|
91
|
+
apiKey: "ad7a5b0b816ca314b69c813ae1368232",
|
|
92
|
+
label: "node",
|
|
93
|
+
dateCreated: "2026-01-19T03:59:35.742Z",
|
|
94
|
+
dateModified: "2026-01-19T03:59:35.742Z",
|
|
95
|
+
publicOrigin: "https://example.com:4321"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
requiredScope: ["tenant-admin"]
|
|
103
|
+
};
|
|
104
|
+
const tenantGetByApiKeyRoute = {
|
|
105
|
+
operationId: "tenantGetByApiKey",
|
|
106
|
+
summary: "Get the tenant by api key",
|
|
107
|
+
tag: tagsTenants[0].name,
|
|
108
|
+
method: "GET",
|
|
109
|
+
path: `${baseRouteName}/api-key/:apiKey`,
|
|
110
|
+
handler: async (httpRequestContext, request) => tenantByApiKey(httpRequestContext, componentName, request),
|
|
111
|
+
responseType: [
|
|
112
|
+
{
|
|
113
|
+
type: "ITenantGetResponse",
|
|
114
|
+
examples: [
|
|
115
|
+
{
|
|
116
|
+
id: "tenantGetByApiKeyResponse",
|
|
117
|
+
description: "The response for the get tenant by api key request.",
|
|
118
|
+
response: {
|
|
119
|
+
body: {
|
|
120
|
+
id: "2a39d6e62d98aa5372432f2e5d2208d4",
|
|
121
|
+
apiKey: "ad7a5b0b816ca314b69c813ae1368232",
|
|
122
|
+
label: "node",
|
|
123
|
+
dateCreated: "2026-01-19T03:59:35.742Z",
|
|
124
|
+
dateModified: "2026-01-19T03:59:35.742Z",
|
|
125
|
+
publicOrigin: "https://example.com:4321"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
requiredScope: ["tenant-admin"]
|
|
133
|
+
};
|
|
134
|
+
const tenantGetByPublicOriginRoute = {
|
|
135
|
+
operationId: "tenantGetByPublicOrigin",
|
|
136
|
+
summary: "Get the tenant by public origin",
|
|
137
|
+
tag: tagsTenants[0].name,
|
|
138
|
+
method: "GET",
|
|
139
|
+
path: `${baseRouteName}/public-origin/:publicOrigin`,
|
|
140
|
+
handler: async (httpRequestContext, request) => tenantByPublicOrigin(httpRequestContext, componentName, request),
|
|
141
|
+
responseType: [
|
|
142
|
+
{
|
|
143
|
+
type: "ITenantGetResponse",
|
|
144
|
+
examples: [
|
|
145
|
+
{
|
|
146
|
+
id: "tenantGetByPublicOriginResponse",
|
|
147
|
+
description: "The response for the get tenant by public origin request.",
|
|
148
|
+
response: {
|
|
149
|
+
body: {
|
|
150
|
+
id: "2a39d6e62d98aa5372432f2e5d2208d4",
|
|
151
|
+
apiKey: "ad7a5b0b816ca314b69c813ae1368232",
|
|
152
|
+
label: "node",
|
|
153
|
+
dateCreated: "2026-01-19T03:59:35.742Z",
|
|
154
|
+
publicOrigin: "https://example.com:4321"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
requiredScope: ["tenant-admin"]
|
|
162
|
+
};
|
|
163
|
+
const tenantRemoveRoute = {
|
|
164
|
+
operationId: "tenantRemove",
|
|
165
|
+
summary: "Remove the tenant by id",
|
|
166
|
+
tag: tagsTenants[0].name,
|
|
167
|
+
method: "DELETE",
|
|
168
|
+
path: `${baseRouteName}/:id`,
|
|
169
|
+
handler: async (httpRequestContext, request) => tenantRemove(httpRequestContext, componentName, request),
|
|
170
|
+
requestType: {
|
|
171
|
+
type: "ITenantRemoveRequest",
|
|
172
|
+
examples: [
|
|
173
|
+
{
|
|
174
|
+
id: "tenantRemoveRequestExample",
|
|
175
|
+
description: "The request for the remove tenant by id request.",
|
|
176
|
+
request: {
|
|
177
|
+
pathParams: {
|
|
178
|
+
id: "2a39d6e62d98aa5372432f2e5d2208d4"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
responseType: [
|
|
185
|
+
{
|
|
186
|
+
type: "INoContentResponse"
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
requiredScope: ["tenant-admin"]
|
|
190
|
+
};
|
|
191
|
+
const tenantCreateRoute = {
|
|
192
|
+
operationId: "tenantCreate",
|
|
193
|
+
summary: "Create a new tenant",
|
|
194
|
+
tag: tagsTenants[0].name,
|
|
195
|
+
method: "POST",
|
|
196
|
+
path: `${baseRouteName}/`,
|
|
197
|
+
handler: async (httpRequestContext, request) => tenantCreate(httpRequestContext, componentName, request),
|
|
198
|
+
requestType: {
|
|
199
|
+
type: "ITenantCreateRequest",
|
|
200
|
+
examples: [
|
|
201
|
+
{
|
|
202
|
+
id: "tenantCreateRequestExample",
|
|
203
|
+
description: "The request for the create tenant request.",
|
|
204
|
+
request: {
|
|
205
|
+
body: {
|
|
206
|
+
apiKey: "ad7a5b0b816ca314b69c813ae1368232",
|
|
207
|
+
label: "node",
|
|
208
|
+
publicOrigin: "https://example.com:4321"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
},
|
|
214
|
+
responseType: [
|
|
215
|
+
{
|
|
216
|
+
type: "ICreatedResponse"
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
requiredScope: ["tenant-admin"]
|
|
220
|
+
};
|
|
221
|
+
const tenantUpdateRoute = {
|
|
222
|
+
operationId: "tenantUpdate",
|
|
223
|
+
summary: "Update an existing tenant",
|
|
224
|
+
tag: tagsTenants[0].name,
|
|
225
|
+
method: "PUT",
|
|
226
|
+
path: `${baseRouteName}/:id`,
|
|
227
|
+
handler: async (httpRequestContext, request) => tenantUpdate(httpRequestContext, componentName, request),
|
|
228
|
+
requestType: {
|
|
229
|
+
type: "ITenantUpdateRequest",
|
|
230
|
+
examples: [
|
|
231
|
+
{
|
|
232
|
+
id: "tenantUpdateRequestExample",
|
|
233
|
+
description: "The request for the update tenant request.",
|
|
234
|
+
request: {
|
|
235
|
+
pathParams: {
|
|
236
|
+
id: "2a39d6e62d98aa5372432f2e5d2208d4"
|
|
237
|
+
},
|
|
238
|
+
body: {
|
|
239
|
+
apiKey: "ad7a5b0b816ca314b69c813ae1368232",
|
|
240
|
+
label: "node",
|
|
241
|
+
publicOrigin: "https://example.com:4321"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
},
|
|
247
|
+
responseType: [
|
|
248
|
+
{
|
|
249
|
+
type: "INoContentResponse"
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
requiredScope: ["tenant-admin"]
|
|
253
|
+
};
|
|
254
|
+
return [
|
|
255
|
+
tenantListRoute,
|
|
256
|
+
tenantCreateRoute,
|
|
257
|
+
tenantGetByIdRoute,
|
|
258
|
+
tenantGetByApiKeyRoute,
|
|
259
|
+
tenantGetByPublicOriginRoute,
|
|
260
|
+
tenantRemoveRoute,
|
|
261
|
+
tenantUpdateRoute
|
|
262
|
+
];
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Get the list of tenants.
|
|
266
|
+
* @param httpRequestContext The request context for the API.
|
|
267
|
+
* @param componentName The name of the component to use in the routes.
|
|
268
|
+
* @param request The request.
|
|
269
|
+
* @returns The response object with additional http response properties.
|
|
270
|
+
*/
|
|
271
|
+
export async function tenantList(httpRequestContext, componentName, request) {
|
|
272
|
+
const hostingComponent = ComponentFactory.get(httpRequestContext.hostingComponentType ?? "hosting");
|
|
273
|
+
const component = ComponentFactory.get(componentName);
|
|
274
|
+
const result = await component.query(HttpParameterHelper.arrayFromString(request.query?.properties), request.query?.cursor, Coerce.integer(request.query?.limit));
|
|
275
|
+
const headers = {};
|
|
276
|
+
if (Is.stringValue(result.cursor)) {
|
|
277
|
+
headers[HeaderTypes.Link] = HeaderHelper.createLinkHeader(await hostingComponent.buildPublicUrl(httpRequestContext.serverRequest.url), { cursor: result.cursor }, "next");
|
|
278
|
+
}
|
|
279
|
+
const defaultPublicOrigin = await hostingComponent.getPublicOrigin(httpRequestContext.serverRequest.url);
|
|
280
|
+
return {
|
|
281
|
+
headers,
|
|
282
|
+
body: result.tenants.map(t => ({ ...t, publicOrigin: t.publicOrigin ?? defaultPublicOrigin }))
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get the tenant by id.
|
|
287
|
+
* @param httpRequestContext The request context for the API.
|
|
288
|
+
* @param componentName The name of the component to use in the routes.
|
|
289
|
+
* @param request The request.
|
|
290
|
+
* @returns The response object with additional http response properties.
|
|
291
|
+
*/
|
|
292
|
+
export async function tenantById(httpRequestContext, componentName, request) {
|
|
293
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
294
|
+
const component = ComponentFactory.get(componentName);
|
|
295
|
+
const hostingComponent = ComponentFactory.get(httpRequestContext.hostingComponentType ?? "hosting");
|
|
296
|
+
const result = await component.get(request.pathParams.id);
|
|
297
|
+
if (!Is.stringValue(result.publicOrigin)) {
|
|
298
|
+
const defaultPublicOrigin = await hostingComponent.getPublicOrigin(httpRequestContext.serverRequest.url);
|
|
299
|
+
result.publicOrigin ??= defaultPublicOrigin;
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
body: result
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Get the tenant by api key.
|
|
307
|
+
* @param httpRequestContext The request context for the API.
|
|
308
|
+
* @param componentName The name of the component to use in the routes.
|
|
309
|
+
* @param request The request.
|
|
310
|
+
* @returns The response object with additional http response properties.
|
|
311
|
+
*/
|
|
312
|
+
export async function tenantByApiKey(httpRequestContext, componentName, request) {
|
|
313
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.apiKey", request.pathParams.apiKey);
|
|
314
|
+
const component = ComponentFactory.get(componentName);
|
|
315
|
+
const hostingComponent = ComponentFactory.get(httpRequestContext.hostingComponentType ?? "hosting");
|
|
316
|
+
const result = await component.getByApiKey(request.pathParams.apiKey);
|
|
317
|
+
if (!Is.stringValue(result.publicOrigin)) {
|
|
318
|
+
const defaultPublicOrigin = await hostingComponent.getPublicOrigin(httpRequestContext.serverRequest.url);
|
|
319
|
+
result.publicOrigin ??= defaultPublicOrigin;
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
body: result
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Get the tenant by public origin.
|
|
327
|
+
* @param httpRequestContext The request context for the API.
|
|
328
|
+
* @param componentName The name of the component to use in the routes.
|
|
329
|
+
* @param request The request.
|
|
330
|
+
* @returns The response object with additional http response properties.
|
|
331
|
+
*/
|
|
332
|
+
export async function tenantByPublicOrigin(httpRequestContext, componentName, request) {
|
|
333
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.publicOrigin", request.pathParams.publicOrigin);
|
|
334
|
+
const component = ComponentFactory.get(componentName);
|
|
335
|
+
const hostingComponent = ComponentFactory.get(httpRequestContext.hostingComponentType ?? "hosting");
|
|
336
|
+
const result = await component.getByPublicOrigin(request.pathParams.publicOrigin);
|
|
337
|
+
if (!Is.stringValue(result.publicOrigin)) {
|
|
338
|
+
const defaultPublicOrigin = await hostingComponent.getPublicOrigin(httpRequestContext.serverRequest.url);
|
|
339
|
+
result.publicOrigin ??= defaultPublicOrigin;
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
body: result
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Remove the tenant by id.
|
|
347
|
+
* @param httpRequestContext The request context for the API.
|
|
348
|
+
* @param componentName The name of the component to use in the routes.
|
|
349
|
+
* @param request The request.
|
|
350
|
+
* @returns The response object with additional http response properties.
|
|
351
|
+
*/
|
|
352
|
+
export async function tenantRemove(httpRequestContext, componentName, request) {
|
|
353
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
354
|
+
const component = ComponentFactory.get(componentName);
|
|
355
|
+
await component.remove(request.pathParams.id);
|
|
356
|
+
return {
|
|
357
|
+
statusCode: HttpStatusCode.noContent
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Create the tenant.
|
|
362
|
+
* @param httpRequestContext The request context for the API.
|
|
363
|
+
* @param componentName The name of the component to use in the routes.
|
|
364
|
+
* @param request The request.
|
|
365
|
+
* @returns The response object with additional http response properties.
|
|
366
|
+
*/
|
|
367
|
+
export async function tenantCreate(httpRequestContext, componentName, request) {
|
|
368
|
+
Guards.stringValue(ROUTES_SOURCE, "request.body", request.body);
|
|
369
|
+
const component = ComponentFactory.get(componentName);
|
|
370
|
+
const createdId = await component.create(request.body);
|
|
371
|
+
return {
|
|
372
|
+
statusCode: HttpStatusCode.created,
|
|
373
|
+
headers: {
|
|
374
|
+
[HeaderTypes.Location]: createdId
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Update the tenant.
|
|
380
|
+
* @param httpRequestContext The request context for the API.
|
|
381
|
+
* @param componentName The name of the component to use in the routes.
|
|
382
|
+
* @param request The request.
|
|
383
|
+
* @returns The response object with additional http response properties.
|
|
384
|
+
*/
|
|
385
|
+
export async function tenantUpdate(httpRequestContext, componentName, request) {
|
|
386
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
387
|
+
Guards.stringValue(ROUTES_SOURCE, "request.body", request.body);
|
|
388
|
+
const component = ComponentFactory.get(componentName);
|
|
389
|
+
await component.update(request.body);
|
|
390
|
+
return {
|
|
391
|
+
statusCode: HttpStatusCode.noContent
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
//# sourceMappingURL=tenantRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenantRoutes.js","sourceRoot":"","sources":["../../src/tenantRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpParameterHelper,\n\ttype ICreatedResponse,\n\ttype IHostingComponent,\n\ttype IHttpRequestContext,\n\ttype INoContentResponse,\n\ttype IRestRoute,\n\ttype ITag,\n\ttype ITenantAdminComponent\n} from \"@twin.org/api-models\";\nimport { Coerce, ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HeaderHelper, HeaderTypes, HttpStatusCode } from \"@twin.org/web\";\nimport type { ITenantCreateRequest } from \"./models/api/ITenantCreateRequest.js\";\nimport type { ITenantGetByApiKeyRequest } from \"./models/api/ITenantGetByApiKeyRequest.js\";\nimport type { ITenantGetByIdRequest } from \"./models/api/ITenantGetByIdRequest.js\";\nimport type { ITenantGetByPublicOriginRequest } from \"./models/api/ITenantGetByPublicOriginRequest.js\";\nimport type { ITenantGetResponse } from \"./models/api/ITenantGetResponse.js\";\nimport type { ITenantListRequest } from \"./models/api/ITenantListRequest.js\";\nimport type { ITenantListResponse } from \"./models/api/ITenantListResponse.js\";\nimport type { ITenantRemoveRequest } from \"./models/api/ITenantRemoveRequest.js\";\nimport type { ITenantUpdateRequest } from \"./models/api/ITenantUpdateRequest.js\";\n\n/**\n * The source used when communicating about these routes.\n */\nconst ROUTES_SOURCE = \"tenantRoutes\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsTenants: ITag[] = [\n\t{\n\t\tname: \"Tenants\",\n\t\tdescription: \"Tenants endpoints for the REST server.\"\n\t}\n];\n\n/**\n * The REST routes for tenant management.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesTenants(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst tenantListRoute: IRestRoute<ITenantListRequest, ITenantListResponse> = {\n\t\toperationId: \"tenantList\",\n\t\tsummary: \"Get the list of tenants\",\n\t\ttag: tagsTenants[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\ttenantList(httpRequestContext, componentName, request),\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ITenantListResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"tenantListResponse\",\n\t\t\t\t\t\tdescription: \"The response for the list tenants request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\t[HeaderTypes.Link]: '<https://api.twin.org/tenants/1>; rel=\"next\"'\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tbody: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tid: \"2a39d6e62d98aa5372432f2e5d2208d4\",\n\t\t\t\t\t\t\t\t\tapiKey: \"ad7a5b0b816ca314b69c813ae1368232\",\n\t\t\t\t\t\t\t\t\tlabel: \"node\",\n\t\t\t\t\t\t\t\t\tdateCreated: \"2026-01-19T03:59:35.742Z\",\n\t\t\t\t\t\t\t\t\tdateModified: \"2026-01-19T03:59:35.742Z\",\n\t\t\t\t\t\t\t\t\tpublicOrigin: \"https://example.com:4321\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\trequiredScope: [\"tenant-admin\"]\n\t};\n\n\tconst tenantGetByIdRoute: IRestRoute<ITenantGetByIdRequest, ITenantGetResponse> = {\n\t\toperationId: \"tenantGetById\",\n\t\tsummary: \"Get the tenant by id\",\n\t\ttag: tagsTenants[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/:id`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\ttenantById(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ITenantGetByIdRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"tenantGetByIdRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\tid: \"2a39d6e62d98aa5372432f2e5d2208d4\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ITenantGetResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"tenantGetByIdResponse\",\n\t\t\t\t\t\tdescription: \"The response for the get tenant by id request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\tid: \"2a39d6e62d98aa5372432f2e5d2208d4\",\n\t\t\t\t\t\t\t\tapiKey: \"ad7a5b0b816ca314b69c813ae1368232\",\n\t\t\t\t\t\t\t\tlabel: \"node\",\n\t\t\t\t\t\t\t\tdateCreated: \"2026-01-19T03:59:35.742Z\",\n\t\t\t\t\t\t\t\tdateModified: \"2026-01-19T03:59:35.742Z\",\n\t\t\t\t\t\t\t\tpublicOrigin: \"https://example.com:4321\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\trequiredScope: [\"tenant-admin\"]\n\t};\n\n\tconst tenantGetByApiKeyRoute: IRestRoute<ITenantGetByApiKeyRequest, ITenantGetResponse> = {\n\t\toperationId: \"tenantGetByApiKey\",\n\t\tsummary: \"Get the tenant by api key\",\n\t\ttag: tagsTenants[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/api-key/:apiKey`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\ttenantByApiKey(httpRequestContext, componentName, request),\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ITenantGetResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"tenantGetByApiKeyResponse\",\n\t\t\t\t\t\tdescription: \"The response for the get tenant by api key request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\tid: \"2a39d6e62d98aa5372432f2e5d2208d4\",\n\t\t\t\t\t\t\t\tapiKey: \"ad7a5b0b816ca314b69c813ae1368232\",\n\t\t\t\t\t\t\t\tlabel: \"node\",\n\t\t\t\t\t\t\t\tdateCreated: \"2026-01-19T03:59:35.742Z\",\n\t\t\t\t\t\t\t\tdateModified: \"2026-01-19T03:59:35.742Z\",\n\t\t\t\t\t\t\t\tpublicOrigin: \"https://example.com:4321\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\trequiredScope: [\"tenant-admin\"]\n\t};\n\n\tconst tenantGetByPublicOriginRoute: IRestRoute = {\n\t\toperationId: \"tenantGetByPublicOrigin\",\n\t\tsummary: \"Get the tenant by public origin\",\n\t\ttag: tagsTenants[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/public-origin/:publicOrigin`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\ttenantByPublicOrigin(httpRequestContext, componentName, request),\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ITenantGetResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"tenantGetByPublicOriginResponse\",\n\t\t\t\t\t\tdescription: \"The response for the get tenant by public origin request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\tid: \"2a39d6e62d98aa5372432f2e5d2208d4\",\n\t\t\t\t\t\t\t\tapiKey: \"ad7a5b0b816ca314b69c813ae1368232\",\n\t\t\t\t\t\t\t\tlabel: \"node\",\n\t\t\t\t\t\t\t\tdateCreated: \"2026-01-19T03:59:35.742Z\",\n\t\t\t\t\t\t\t\tpublicOrigin: \"https://example.com:4321\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\trequiredScope: [\"tenant-admin\"]\n\t};\n\n\tconst tenantRemoveRoute: IRestRoute<ITenantRemoveRequest, INoContentResponse> = {\n\t\toperationId: \"tenantRemove\",\n\t\tsummary: \"Remove the tenant by id\",\n\t\ttag: tagsTenants[0].name,\n\t\tmethod: \"DELETE\",\n\t\tpath: `${baseRouteName}/:id`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\ttenantRemove(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ITenantRemoveRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"tenantRemoveRequestExample\",\n\t\t\t\t\tdescription: \"The request for the remove tenant by id request.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\tid: \"2a39d6e62d98aa5372432f2e5d2208d4\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t}\n\t\t],\n\t\trequiredScope: [\"tenant-admin\"]\n\t};\n\n\tconst tenantCreateRoute: IRestRoute<ITenantCreateRequest, ICreatedResponse> = {\n\t\toperationId: \"tenantCreate\",\n\t\tsummary: \"Create a new tenant\",\n\t\ttag: tagsTenants[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\ttenantCreate(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ITenantCreateRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"tenantCreateRequestExample\",\n\t\t\t\t\tdescription: \"The request for the create tenant request.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\tapiKey: \"ad7a5b0b816ca314b69c813ae1368232\",\n\t\t\t\t\t\t\tlabel: \"node\",\n\t\t\t\t\t\t\tpublicOrigin: \"https://example.com:4321\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ICreatedResponse>()\n\t\t\t}\n\t\t],\n\t\trequiredScope: [\"tenant-admin\"]\n\t};\n\n\tconst tenantUpdateRoute: IRestRoute<ITenantUpdateRequest, INoContentResponse> = {\n\t\toperationId: \"tenantUpdate\",\n\t\tsummary: \"Update an existing tenant\",\n\t\ttag: tagsTenants[0].name,\n\t\tmethod: \"PUT\",\n\t\tpath: `${baseRouteName}/:id`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\ttenantUpdate(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ITenantUpdateRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"tenantUpdateRequestExample\",\n\t\t\t\t\tdescription: \"The request for the update tenant request.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\tid: \"2a39d6e62d98aa5372432f2e5d2208d4\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\tapiKey: \"ad7a5b0b816ca314b69c813ae1368232\",\n\t\t\t\t\t\t\tlabel: \"node\",\n\t\t\t\t\t\t\tpublicOrigin: \"https://example.com:4321\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t}\n\t\t],\n\t\trequiredScope: [\"tenant-admin\"]\n\t};\n\n\treturn [\n\t\ttenantListRoute,\n\t\ttenantCreateRoute,\n\t\ttenantGetByIdRoute,\n\t\ttenantGetByApiKeyRoute,\n\t\ttenantGetByPublicOriginRoute,\n\t\ttenantRemoveRoute,\n\t\ttenantUpdateRoute\n\t];\n}\n\n/**\n * Get the list of tenants.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function tenantList(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ITenantListRequest\n): Promise<ITenantListResponse> {\n\tconst hostingComponent = ComponentFactory.get<IHostingComponent>(\n\t\thttpRequestContext.hostingComponentType ?? \"hosting\"\n\t);\n\n\tconst component = ComponentFactory.get<ITenantAdminComponent>(componentName);\n\n\tconst result = await component.query(\n\t\tHttpParameterHelper.arrayFromString(request.query?.properties),\n\t\trequest.query?.cursor,\n\t\tCoerce.integer(request.query?.limit)\n\t);\n\n\tconst headers: ITenantListResponse[\"headers\"] = {};\n\n\tif (Is.stringValue(result.cursor)) {\n\t\theaders[HeaderTypes.Link] = HeaderHelper.createLinkHeader(\n\t\t\tawait hostingComponent.buildPublicUrl(httpRequestContext.serverRequest.url),\n\t\t\t{ cursor: result.cursor },\n\t\t\t\"next\"\n\t\t);\n\t}\n\n\tconst defaultPublicOrigin = await hostingComponent.getPublicOrigin(\n\t\thttpRequestContext.serverRequest.url\n\t);\n\n\treturn {\n\t\theaders,\n\t\tbody: result.tenants.map(t => ({ ...t, publicOrigin: t.publicOrigin ?? defaultPublicOrigin }))\n\t};\n}\n\n/**\n * Get the tenant by id.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function tenantById(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ITenantGetByIdRequest\n): Promise<ITenantGetResponse> {\n\tGuards.stringValue(ROUTES_SOURCE, nameof(request.pathParams.id), request.pathParams.id);\n\tconst component = ComponentFactory.get<ITenantAdminComponent>(componentName);\n\n\tconst hostingComponent = ComponentFactory.get<IHostingComponent>(\n\t\thttpRequestContext.hostingComponentType ?? \"hosting\"\n\t);\n\n\tconst result = await component.get(request.pathParams.id);\n\n\tif (!Is.stringValue(result.publicOrigin)) {\n\t\tconst defaultPublicOrigin = await hostingComponent.getPublicOrigin(\n\t\t\thttpRequestContext.serverRequest.url\n\t\t);\n\n\t\tresult.publicOrigin ??= defaultPublicOrigin;\n\t}\n\n\treturn {\n\t\tbody: result\n\t};\n}\n\n/**\n * Get the tenant by api key.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function tenantByApiKey(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ITenantGetByApiKeyRequest\n): Promise<ITenantGetResponse> {\n\tGuards.stringValue(ROUTES_SOURCE, nameof(request.pathParams.apiKey), request.pathParams.apiKey);\n\tconst component = ComponentFactory.get<ITenantAdminComponent>(componentName);\n\n\tconst hostingComponent = ComponentFactory.get<IHostingComponent>(\n\t\thttpRequestContext.hostingComponentType ?? \"hosting\"\n\t);\n\n\tconst result = await component.getByApiKey(request.pathParams.apiKey);\n\n\tif (!Is.stringValue(result.publicOrigin)) {\n\t\tconst defaultPublicOrigin = await hostingComponent.getPublicOrigin(\n\t\t\thttpRequestContext.serverRequest.url\n\t\t);\n\n\t\tresult.publicOrigin ??= defaultPublicOrigin;\n\t}\n\n\treturn {\n\t\tbody: result\n\t};\n}\n\n/**\n * Get the tenant by public origin.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function tenantByPublicOrigin(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ITenantGetByPublicOriginRequest\n): Promise<ITenantGetResponse> {\n\tGuards.stringValue(\n\t\tROUTES_SOURCE,\n\t\tnameof(request.pathParams.publicOrigin),\n\t\trequest.pathParams.publicOrigin\n\t);\n\tconst component = ComponentFactory.get<ITenantAdminComponent>(componentName);\n\n\tconst hostingComponent = ComponentFactory.get<IHostingComponent>(\n\t\thttpRequestContext.hostingComponentType ?? \"hosting\"\n\t);\n\n\tconst result = await component.getByPublicOrigin(request.pathParams.publicOrigin);\n\n\tif (!Is.stringValue(result.publicOrigin)) {\n\t\tconst defaultPublicOrigin = await hostingComponent.getPublicOrigin(\n\t\t\thttpRequestContext.serverRequest.url\n\t\t);\n\n\t\tresult.publicOrigin ??= defaultPublicOrigin;\n\t}\n\n\treturn {\n\t\tbody: result\n\t};\n}\n\n/**\n * Remove the tenant by id.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function tenantRemove(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ITenantRemoveRequest\n): Promise<INoContentResponse> {\n\tGuards.stringValue(ROUTES_SOURCE, nameof(request.pathParams.id), request.pathParams.id);\n\tconst component = ComponentFactory.get<ITenantAdminComponent>(componentName);\n\n\tawait component.remove(request.pathParams.id);\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n\n/**\n * Create the tenant.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function tenantCreate(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ITenantCreateRequest\n): Promise<ICreatedResponse> {\n\tGuards.stringValue(ROUTES_SOURCE, nameof(request.body), request.body);\n\tconst component = ComponentFactory.get<ITenantAdminComponent>(componentName);\n\n\tconst createdId = await component.create(request.body);\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.created,\n\t\theaders: {\n\t\t\t[HeaderTypes.Location]: createdId\n\t\t}\n\t};\n}\n\n/**\n * Update the tenant.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function tenantUpdate(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ITenantUpdateRequest\n): Promise<INoContentResponse> {\n\tGuards.stringValue(ROUTES_SOURCE, nameof(request.pathParams.id), request.pathParams.id);\n\tGuards.stringValue(ROUTES_SOURCE, nameof(request.body), request.body);\n\tconst component = ComponentFactory.get<ITenantAdminComponent>(componentName);\n\n\tawait component.update(request.body);\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n"]}
|