@twin.org/api-tenant-processor 0.0.3-next.2 → 0.0.3-next.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/entities/tenant.js +25 -1
- package/dist/es/entities/tenant.js.map +1 -1
- package/dist/es/index.js +10 -2
- package/dist/es/index.js.map +1 -1
- 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 +112 -15
- package/dist/es/tenantAdminService.js.map +1 -1
- package/dist/es/tenantIdContextIdHandler.js +8 -1
- package/dist/es/tenantIdContextIdHandler.js.map +1 -1
- package/dist/es/tenantProcessor.js +26 -21
- package/dist/es/tenantProcessor.js.map +1 -1
- package/dist/es/tenantRoutes.js +396 -0
- package/dist/es/tenantRoutes.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 +12 -0
- package/dist/types/index.d.ts +10 -2
- 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 +18 -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 +30 -10
- package/dist/types/tenantIdContextIdHandler.d.ts +5 -0
- package/dist/types/tenantRoutes.d.ts +77 -0
- package/docs/changelog.md +278 -0
- package/docs/reference/classes/Tenant.md +24 -0
- package/docs/reference/classes/TenantAdminService.md +93 -19
- package/docs/reference/classes/TenantIdContextIdHandler.md +18 -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 +21 -2
- 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 +23 -0
- package/docs/reference/interfaces/ITenantListResponse.md +23 -0
- package/docs/reference/interfaces/ITenantRemoveRequest.md +17 -0
- package/docs/reference/interfaces/ITenantUpdateRequest.md +25 -0
- package/docs/reference/variables/tagsTenants.md +5 -0
- package/locales/en.json +4 -0
- package/package.json +2 -2
- 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
|
@@ -21,13 +21,25 @@ let Tenant = class Tenant {
|
|
|
21
21
|
* The date the tenant was created.
|
|
22
22
|
*/
|
|
23
23
|
dateCreated;
|
|
24
|
+
/**
|
|
25
|
+
* The date the tenant was modified.
|
|
26
|
+
*/
|
|
27
|
+
dateModified;
|
|
28
|
+
/**
|
|
29
|
+
* The origin available to the public for accessing the API.
|
|
30
|
+
*/
|
|
31
|
+
publicOrigin;
|
|
32
|
+
/**
|
|
33
|
+
* Indicates whether the tenant is the node tenant.
|
|
34
|
+
*/
|
|
35
|
+
isNodeTenant;
|
|
24
36
|
};
|
|
25
37
|
__decorate([
|
|
26
38
|
property({ type: "string", isPrimary: true }),
|
|
27
39
|
__metadata("design:type", String)
|
|
28
40
|
], Tenant.prototype, "id", void 0);
|
|
29
41
|
__decorate([
|
|
30
|
-
property({ type: "string" }),
|
|
42
|
+
property({ type: "string", isSecondary: true }),
|
|
31
43
|
__metadata("design:type", String)
|
|
32
44
|
], Tenant.prototype, "apiKey", void 0);
|
|
33
45
|
__decorate([
|
|
@@ -38,6 +50,18 @@ __decorate([
|
|
|
38
50
|
property({ type: "string" }),
|
|
39
51
|
__metadata("design:type", String)
|
|
40
52
|
], Tenant.prototype, "dateCreated", void 0);
|
|
53
|
+
__decorate([
|
|
54
|
+
property({ type: "string" }),
|
|
55
|
+
__metadata("design:type", String)
|
|
56
|
+
], Tenant.prototype, "dateModified", void 0);
|
|
57
|
+
__decorate([
|
|
58
|
+
property({ type: "string", optional: true, isSecondary: true }),
|
|
59
|
+
__metadata("design:type", String)
|
|
60
|
+
], Tenant.prototype, "publicOrigin", void 0);
|
|
61
|
+
__decorate([
|
|
62
|
+
property({ type: "boolean" }),
|
|
63
|
+
__metadata("design:type", Boolean)
|
|
64
|
+
], Tenant.prototype, "isNodeTenant", void 0);
|
|
41
65
|
Tenant = __decorate([
|
|
42
66
|
entity()
|
|
43
67
|
], Tenant);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../../src/entities/tenant.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;GAEG;AAEI,IAAM,MAAM,GAAZ,MAAM,MAAM;IAClB;;OAEG;IAEI,EAAE,CAAU;IAEnB;;OAEG;IAEI,MAAM,CAAU;IAEvB;;OAEG;IAEI,KAAK,CAAU;IAEtB;;OAEG;IAEI,WAAW,CAAU;
|
|
1
|
+
{"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../../src/entities/tenant.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;GAEG;AAEI,IAAM,MAAM,GAAZ,MAAM,MAAM;IAClB;;OAEG;IAEI,EAAE,CAAU;IAEnB;;OAEG;IAEI,MAAM,CAAU;IAEvB;;OAEG;IAEI,KAAK,CAAU;IAEtB;;OAEG;IAEI,WAAW,CAAU;IAE5B;;OAEG;IAEI,YAAY,CAAU;IAE7B;;OAEG;IAEI,YAAY,CAAU;IAE7B;;OAEG;IAEI,YAAY,CAAW;CAC9B,CAAA;AArCO;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;;kCAC3B;AAMZ;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;;sCACzB;AAMhB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;qCACP;AAMf;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;2CACD;AAMrB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;4CACA;AAMtB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;;4CACnC;AAMtB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;4CACA;AAzClB,MAAM;IADlB,MAAM,EAAE;GACI,MAAM,CA0ClB","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { entity, property } from \"@twin.org/entity\";\n\n/**\n * Class defining the storage for node tenants.\n */\n@entity()\nexport class Tenant {\n\t/**\n\t * The unique identifier for the tenant.\n\t */\n\t@property({ type: \"string\", isPrimary: true })\n\tpublic id!: string;\n\n\t/**\n\t * The api key for the tenant.\n\t */\n\t@property({ type: \"string\", isSecondary: true })\n\tpublic apiKey!: string;\n\n\t/**\n\t * The label of the tenant.\n\t */\n\t@property({ type: \"string\" })\n\tpublic label!: string;\n\n\t/**\n\t * The date the tenant was created.\n\t */\n\t@property({ type: \"string\" })\n\tpublic dateCreated!: string;\n\n\t/**\n\t * The date the tenant was modified.\n\t */\n\t@property({ type: \"string\" })\n\tpublic dateModified!: string;\n\n\t/**\n\t * The origin available to the public for accessing the API.\n\t */\n\t@property({ type: \"string\", optional: true, isSecondary: true })\n\tpublic publicOrigin?: string;\n\n\t/**\n\t * Indicates whether the tenant is the node tenant.\n\t */\n\t@property({ type: \"boolean\" })\n\tpublic isNodeTenant!: boolean;\n}\n"]}
|
package/dist/es/index.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
// Copyright 2025 IOTA Stiftung.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
3
|
export * from "./entities/tenant.js";
|
|
4
|
-
export * from "./models/
|
|
5
|
-
export * from "./models/
|
|
4
|
+
export * from "./models/api/ITenantCreateRequest.js";
|
|
5
|
+
export * from "./models/api/ITenantGetByApiKeyRequest.js";
|
|
6
|
+
export * from "./models/api/ITenantGetByIdRequest.js";
|
|
7
|
+
export * from "./models/api/ITenantGetByPublicOriginRequest.js";
|
|
8
|
+
export * from "./models/api/ITenantGetResponse.js";
|
|
9
|
+
export * from "./models/api/ITenantListRequest.js";
|
|
10
|
+
export * from "./models/api/ITenantListResponse.js";
|
|
11
|
+
export * from "./models/api/ITenantRemoveRequest.js";
|
|
12
|
+
export * from "./models/api/ITenantUpdateRequest.js";
|
|
6
13
|
export * from "./models/ITenantAdminServiceConfig.js";
|
|
7
14
|
export * from "./models/ITenantAdminServiceConstructorOptions.js";
|
|
8
15
|
export * from "./models/ITenantProcessorConfig.js";
|
|
@@ -11,5 +18,6 @@ export * from "./schema.js";
|
|
|
11
18
|
export * from "./tenantAdminService.js";
|
|
12
19
|
export * from "./tenantIdContextIdHandler.js";
|
|
13
20
|
export * from "./tenantProcessor.js";
|
|
21
|
+
export * from "./tenantRoutes.js";
|
|
14
22
|
export * from "./utils/tenantIdHelper.js";
|
|
15
23
|
//# sourceMappingURL=index.js.map
|
package/dist/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sCAAsC,CAAC;AACrD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,uCAAuC,CAAC;AACtD,cAAc,iDAAiD,CAAC;AAChE,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,mDAAmD,CAAC;AAClE,cAAc,oCAAoC,CAAC;AACnD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,aAAa,CAAC;AAC5B,cAAc,yBAAyB,CAAC;AACxC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./entities/tenant.js\";\nexport * from \"./models/api/ITenantCreateRequest.js\";\nexport * from \"./models/api/ITenantGetByApiKeyRequest.js\";\nexport * from \"./models/api/ITenantGetByIdRequest.js\";\nexport * from \"./models/api/ITenantGetByPublicOriginRequest.js\";\nexport * from \"./models/api/ITenantGetResponse.js\";\nexport * from \"./models/api/ITenantListRequest.js\";\nexport * from \"./models/api/ITenantListResponse.js\";\nexport * from \"./models/api/ITenantRemoveRequest.js\";\nexport * from \"./models/api/ITenantUpdateRequest.js\";\nexport * from \"./models/ITenantAdminServiceConfig.js\";\nexport * from \"./models/ITenantAdminServiceConstructorOptions.js\";\nexport * from \"./models/ITenantProcessorConfig.js\";\nexport * from \"./models/ITenantProcessorConstructorOptions.js\";\nexport * from \"./schema.js\";\nexport * from \"./tenantAdminService.js\";\nexport * from \"./tenantIdContextIdHandler.js\";\nexport * from \"./tenantProcessor.js\";\nexport * from \"./tenantRoutes.js\";\nexport * from \"./utils/tenantIdHelper.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITenantCreateRequest.js","sourceRoot":"","sources":["../../../../src/models/api/ITenantCreateRequest.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITenant } from \"@twin.org/api-models\";\n\n/**\n * The tenant to create.\n */\nexport interface ITenantCreateRequest {\n\t/**\n\t * The tenant to create.\n\t */\n\tbody: Omit<ITenant, \"id\" | \"dateCreated\" | \"dateModified\"> & { id?: string };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITenantGetByApiKeyRequest.js","sourceRoot":"","sources":["../../../../src/models/api/ITenantGetByApiKeyRequest.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The tenant to get by API key.\n */\nexport interface ITenantGetByApiKeyRequest {\n\t/**\n\t * The path parameters.\n\t */\n\tpathParams: {\n\t\t/**\n\t\t * The API key of the tenant to get.\n\t\t */\n\t\tapiKey: string;\n\t};\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITenantGetByIdRequest.js","sourceRoot":"","sources":["../../../../src/models/api/ITenantGetByIdRequest.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The tenant to get by id.\n */\nexport interface ITenantGetByIdRequest {\n\t/**\n\t * The path parameters.\n\t */\n\tpathParams: {\n\t\t/**\n\t\t * The id of the tenant to get.\n\t\t */\n\t\tid: string;\n\t};\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITenantGetByPublicOriginRequest.js","sourceRoot":"","sources":["../../../../src/models/api/ITenantGetByPublicOriginRequest.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The tenant to get by public origin.\n */\nexport interface ITenantGetByPublicOriginRequest {\n\t/**\n\t * The path parameters.\n\t */\n\tpathParams: {\n\t\t/**\n\t\t * The public origin of the tenant to get.\n\t\t */\n\t\tpublicOrigin: string;\n\t};\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITenantGetResponse.js","sourceRoot":"","sources":["../../../../src/models/api/ITenantGetResponse.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITenant } from \"@twin.org/api-models\";\n\n/**\n * The tenant get response.\n */\nexport interface ITenantGetResponse {\n\t/**\n\t * The tenant.\n\t */\n\tbody: ITenant;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITenantListRequest.js","sourceRoot":"","sources":["../../../../src/models/api/ITenantListRequest.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The list of tenants.\n */\nexport interface ITenantListRequest {\n\t/**\n\t * The query parameters.\n\t */\n\tquery: {\n\t\t/**\n\t\t * The cursor to get the next chunk of tenants.\n\t\t */\n\t\tcursor?: string;\n\n\t\t/**\n\t\t * The number of tenants to return.\n\t\t */\n\t\tlimit?: string;\n\t};\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITenantListResponse.js","sourceRoot":"","sources":["../../../../src/models/api/ITenantListResponse.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITenant } from \"@twin.org/api-models\";\nimport type { HeaderTypes } from \"@twin.org/web\";\n\n/**\n * The list of tenants.\n */\nexport interface ITenantListResponse {\n\t/**\n\t * The headers which can be used to include the cursor.\n\t */\n\theaders?: {\n\t\t[HeaderTypes.Link]?: string | string[];\n\t};\n\n\t/**\n\t * The list of tenants.\n\t */\n\tbody: ITenant[];\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITenantRemoveRequest.js","sourceRoot":"","sources":["../../../../src/models/api/ITenantRemoveRequest.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The tenant to remove by id.\n */\nexport interface ITenantRemoveRequest {\n\t/**\n\t * The path parameters.\n\t */\n\tpathParams: {\n\t\t/**\n\t\t * The id of the tenant to remove.\n\t\t */\n\t\tid: string;\n\t};\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITenantUpdateRequest.js","sourceRoot":"","sources":["../../../../src/models/api/ITenantUpdateRequest.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITenant } from \"@twin.org/api-models\";\n\n/**\n * The tenant to update.\n */\nexport interface ITenantUpdateRequest {\n\t/**\n\t * The path parameters.\n\t */\n\tpathParams: {\n\t\t/**\n\t\t * The id of the tenant to update.\n\t\t */\n\t\tid: string;\n\t};\n\n\t/**\n\t * The tenant to update.\n\t */\n\tbody: Omit<ITenant, \"id\" | \"dateCreated\" | \"dateModified\">;\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { generateRestRoutesTenants, tagsTenants } from "./tenantRoutes.js";
|
|
2
|
+
export const restEntryPoints = [
|
|
3
|
+
{
|
|
4
|
+
name: "tenants",
|
|
5
|
+
defaultBaseRoute: "tenants",
|
|
6
|
+
tags: tagsTenants,
|
|
7
|
+
generateRoutes: generateRestRoutesTenants
|
|
8
|
+
}
|
|
9
|
+
];
|
|
10
|
+
//# sourceMappingURL=restEntryPoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restEntryPoints.js","sourceRoot":"","sources":["../../src/restEntryPoints.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE3E,MAAM,CAAC,MAAM,eAAe,GAA2B;IACtD;QACC,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,IAAI,EAAE,WAAW;QACjB,cAAc,EAAE,yBAAyB;KACzC;CACD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IRestRouteEntryPoint } from \"@twin.org/api-models\";\nimport { generateRestRoutesTenants, tagsTenants } from \"./tenantRoutes.js\";\n\nexport const restEntryPoints: IRestRouteEntryPoint[] = [\n\t{\n\t\tname: \"tenants\",\n\t\tdefaultBaseRoute: \"tenants\",\n\t\ttags: tagsTenants,\n\t\tgenerateRoutes: generateRestRoutesTenants\n\t}\n];\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { Guards } from "@twin.org/core";
|
|
1
|
+
import { GeneralError, Guards, Is, Url, NotFoundError } from "@twin.org/core";
|
|
2
|
+
import { ComparisonOperator } from "@twin.org/entity";
|
|
4
3
|
import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
|
|
5
4
|
import { Tenant } from "./entities/tenant.js";
|
|
5
|
+
import { TenantIdHelper } from "./utils/tenantIdHelper.js";
|
|
6
6
|
/**
|
|
7
7
|
* Service for performing email messaging operations to a connector.
|
|
8
8
|
*/
|
|
@@ -33,43 +33,130 @@ export class TenantAdminService {
|
|
|
33
33
|
/**
|
|
34
34
|
* Get a tenant by its id.
|
|
35
35
|
* @param tenantId The id of the tenant.
|
|
36
|
-
* @returns The tenant
|
|
36
|
+
* @returns The tenant.
|
|
37
|
+
* @throws Error if the tenant is not found.
|
|
37
38
|
*/
|
|
38
39
|
async get(tenantId) {
|
|
39
|
-
Guards.
|
|
40
|
+
Guards.stringHexLength(TenantAdminService.CLASS_NAME, "tenantId", tenantId, 32);
|
|
40
41
|
let tenant;
|
|
41
42
|
try {
|
|
42
43
|
tenant = await this._entityStorageConnector.get(tenantId);
|
|
43
44
|
}
|
|
44
45
|
catch { }
|
|
46
|
+
if (!Is.object(tenant)) {
|
|
47
|
+
throw new NotFoundError(TenantAdminService.CLASS_NAME, "tenantNotFound", tenantId);
|
|
48
|
+
}
|
|
45
49
|
return tenant;
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
48
52
|
* Get a tenant by its api key.
|
|
49
53
|
* @param apiKey The api key of the tenant.
|
|
50
|
-
* @returns The tenant
|
|
54
|
+
* @returns The tenant.
|
|
55
|
+
* @throws Error if the tenant is not found.
|
|
51
56
|
*/
|
|
52
57
|
async getByApiKey(apiKey) {
|
|
53
|
-
Guards.
|
|
58
|
+
Guards.stringHexLength(TenantAdminService.CLASS_NAME, "apiKey", apiKey, 32);
|
|
54
59
|
let tenant;
|
|
55
60
|
try {
|
|
56
61
|
tenant = await this._entityStorageConnector.get(apiKey, "apiKey");
|
|
57
62
|
}
|
|
58
63
|
catch { }
|
|
64
|
+
if (!Is.object(tenant)) {
|
|
65
|
+
throw new NotFoundError(TenantAdminService.CLASS_NAME, "tenantNotFound", apiKey);
|
|
66
|
+
}
|
|
59
67
|
return tenant;
|
|
60
68
|
}
|
|
61
69
|
/**
|
|
62
|
-
*
|
|
70
|
+
* Get a tenant by its public origin.
|
|
71
|
+
* @param publicOrigin The origin of the tenant.
|
|
72
|
+
* @returns The tenant.
|
|
73
|
+
* @throws Error if the tenant is not found.
|
|
74
|
+
*/
|
|
75
|
+
async getByPublicOrigin(publicOrigin) {
|
|
76
|
+
Guards.stringValue(TenantAdminService.CLASS_NAME, "publicOrigin", publicOrigin);
|
|
77
|
+
let tenant;
|
|
78
|
+
try {
|
|
79
|
+
tenant = await this._entityStorageConnector.get(publicOrigin, "publicOrigin");
|
|
80
|
+
}
|
|
81
|
+
catch { }
|
|
82
|
+
if (!Is.object(tenant)) {
|
|
83
|
+
throw new NotFoundError(TenantAdminService.CLASS_NAME, "tenantNotFound", publicOrigin);
|
|
84
|
+
}
|
|
85
|
+
return tenant;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Create a tenant.
|
|
63
89
|
* @param tenant The tenant to store.
|
|
64
|
-
* @returns
|
|
90
|
+
* @returns The tenant id.
|
|
65
91
|
*/
|
|
66
|
-
async
|
|
92
|
+
async create(tenant) {
|
|
67
93
|
Guards.objectValue(TenantAdminService.CLASS_NAME, "tenant", tenant);
|
|
94
|
+
if (Is.stringValue(tenant.id)) {
|
|
95
|
+
Guards.stringHexLength(TenantAdminService.CLASS_NAME, "tenant.id", tenant.id, 32);
|
|
96
|
+
}
|
|
97
|
+
if (Is.stringValue(tenant.apiKey)) {
|
|
98
|
+
Guards.stringHexLength(TenantAdminService.CLASS_NAME, "tenant.apiKey", tenant.apiKey, 32);
|
|
99
|
+
}
|
|
100
|
+
let publicOrigin;
|
|
101
|
+
if (Is.stringValue(tenant.publicOrigin)) {
|
|
102
|
+
Url.guard(TenantAdminService.CLASS_NAME, "tenant.publicOrigin", tenant.publicOrigin);
|
|
103
|
+
const url = new Url(tenant.publicOrigin);
|
|
104
|
+
const parts = url.parts();
|
|
105
|
+
publicOrigin = `${parts.schema}://${parts.host}${Is.integer(parts.port) ? `:${parts.port}` : ""}`;
|
|
106
|
+
}
|
|
107
|
+
if (Is.stringValue(tenant.apiKey)) {
|
|
108
|
+
const existingApiKey = await this._entityStorageConnector.get(tenant.apiKey, "apiKey");
|
|
109
|
+
if (Is.object(existingApiKey) && existingApiKey.id !== tenant.id) {
|
|
110
|
+
throw new GeneralError(TenantAdminService.CLASS_NAME, "apiKeyAlreadyInUse");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
68
113
|
const tenantEntity = new Tenant();
|
|
69
|
-
tenantEntity.id = tenant.id;
|
|
70
|
-
tenantEntity.apiKey = tenant.apiKey;
|
|
114
|
+
tenantEntity.id = tenant.id ?? TenantIdHelper.generateTenantId();
|
|
115
|
+
tenantEntity.apiKey = tenant.apiKey ?? TenantIdHelper.generateApiKey();
|
|
71
116
|
tenantEntity.dateCreated = new Date(Date.now()).toISOString();
|
|
117
|
+
tenantEntity.dateModified = tenantEntity.dateCreated;
|
|
72
118
|
tenantEntity.label = tenant.label;
|
|
119
|
+
tenantEntity.publicOrigin = publicOrigin;
|
|
120
|
+
tenantEntity.isNodeTenant = tenant.isNodeTenant;
|
|
121
|
+
await this._entityStorageConnector.set(tenantEntity);
|
|
122
|
+
return tenantEntity.id;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Update a tenant.
|
|
126
|
+
* @param tenant The tenant to update.
|
|
127
|
+
* @returns The nothing.
|
|
128
|
+
*/
|
|
129
|
+
async update(tenant) {
|
|
130
|
+
Guards.objectValue(TenantAdminService.CLASS_NAME, "tenant", tenant);
|
|
131
|
+
Guards.stringHexLength(TenantAdminService.CLASS_NAME, "tenant.id", tenant.id, 32);
|
|
132
|
+
if (Is.stringValue(tenant.apiKey)) {
|
|
133
|
+
Guards.stringHexLength(TenantAdminService.CLASS_NAME, "tenant.apiKey", tenant.apiKey, 32);
|
|
134
|
+
}
|
|
135
|
+
let publicOrigin;
|
|
136
|
+
if (Is.stringValue(tenant.publicOrigin)) {
|
|
137
|
+
Url.guard(TenantAdminService.CLASS_NAME, "tenant.publicOrigin", tenant.publicOrigin);
|
|
138
|
+
const url = new Url(tenant.publicOrigin);
|
|
139
|
+
const parts = url.parts();
|
|
140
|
+
publicOrigin = `${parts.schema}://${parts.host}${Is.integer(parts.port) ? `:${parts.port}` : ""}`;
|
|
141
|
+
}
|
|
142
|
+
const currentTenant = await this._entityStorageConnector.get(tenant.id);
|
|
143
|
+
if (!Is.object(currentTenant)) {
|
|
144
|
+
throw new NotFoundError(TenantAdminService.CLASS_NAME, "tenantNotFound", tenant.id);
|
|
145
|
+
}
|
|
146
|
+
if (Is.stringValue(tenant.apiKey)) {
|
|
147
|
+
const existingApiKey = await this._entityStorageConnector.get(tenant.apiKey, "apiKey");
|
|
148
|
+
if (Is.object(existingApiKey) && existingApiKey.id !== currentTenant.id) {
|
|
149
|
+
throw new GeneralError(TenantAdminService.CLASS_NAME, "apiKeyAlreadyInUse");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const tenantEntity = new Tenant();
|
|
153
|
+
tenantEntity.id = tenant.id;
|
|
154
|
+
tenantEntity.apiKey = tenant.apiKey ?? currentTenant.apiKey;
|
|
155
|
+
tenantEntity.dateCreated = currentTenant.dateCreated;
|
|
156
|
+
tenantEntity.dateModified = new Date(Date.now()).toISOString();
|
|
157
|
+
tenantEntity.label = tenant.label ?? currentTenant.label;
|
|
158
|
+
tenantEntity.publicOrigin = publicOrigin ?? currentTenant.publicOrigin;
|
|
159
|
+
tenantEntity.isNodeTenant = tenant.isNodeTenant ?? currentTenant.isNodeTenant;
|
|
73
160
|
await this._entityStorageConnector.set(tenantEntity);
|
|
74
161
|
}
|
|
75
162
|
/**
|
|
@@ -78,17 +165,27 @@ export class TenantAdminService {
|
|
|
78
165
|
* @returns Nothing.
|
|
79
166
|
*/
|
|
80
167
|
async remove(tenantId) {
|
|
81
|
-
Guards.
|
|
168
|
+
Guards.stringHexLength(TenantAdminService.CLASS_NAME, "tenantId", tenantId, 32);
|
|
82
169
|
return this._entityStorageConnector.remove(tenantId);
|
|
83
170
|
}
|
|
84
171
|
/**
|
|
85
172
|
* Query tenants with pagination.
|
|
173
|
+
* @param options Optional query options.
|
|
174
|
+
* @param options.isNodeTenant Whether to filter for node admin tenants.
|
|
86
175
|
* @param cursor The cursor to start from.
|
|
87
176
|
* @param limit The maximum number of tenants to return.
|
|
88
177
|
* @returns The tenants and the next cursor if more tenants are available.
|
|
89
178
|
*/
|
|
90
|
-
async query(cursor, limit) {
|
|
91
|
-
const
|
|
179
|
+
async query(options, cursor, limit) {
|
|
180
|
+
const conditions = [];
|
|
181
|
+
if (Is.boolean(options?.isNodeTenant)) {
|
|
182
|
+
conditions.push({
|
|
183
|
+
property: "isNodeTenant",
|
|
184
|
+
value: options.isNodeTenant,
|
|
185
|
+
comparison: ComparisonOperator.Equals
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
const result = await this._entityStorageConnector.query(conditions.length > 0 ? { conditions } : undefined, undefined, undefined, cursor, limit);
|
|
92
189
|
return {
|
|
93
190
|
tenants: result.entities,
|
|
94
191
|
cursor: result.cursor
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenantAdminService.js","sourceRoot":"","sources":["../../src/tenantAdminService.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EACN,6BAA6B,EAE7B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAK9C;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAC9B;;OAEG;IACI,MAAM,CAAU,UAAU,wBAAwC;IAEzE;;;OAGG;IACc,uBAAuB,CAAkC;IAE1E;;;OAGG;IACH,YAAY,OAA+C;QAC1D,IAAI,CAAC,uBAAuB,GAAG,6BAA6B,CAAC,GAAG,CAC/D,OAAO,EAAE,uBAAuB,IAAI,QAAQ,CAC5C,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,kBAAkB,CAAC,UAAU,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,GAAG,CAAC,QAAgB;QAChC,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,UAAU,cAAoB,QAAQ,CAAC,CAAC;QAE9E,IAAI,MAAM,CAAC;QAEX,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,OAAO,MAAM,CAAC;IACf,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,WAAW,CAAC,MAAc;QACtC,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,UAAU,YAAkB,MAAM,CAAC,CAAC;QAE1E,IAAI,MAAM,CAAC;QAEX,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACnE,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,OAAO,MAAM,CAAC;IACf,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,GAAG,CAAC,MAAe;QAC/B,MAAM,CAAC,WAAW,CAAU,kBAAkB,CAAC,UAAU,YAAkB,MAAM,CAAC,CAAC;QAEnF,MAAM,YAAY,GAAG,IAAI,MAAM,EAAE,CAAC;QAClC,YAAY,CAAC,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;QAC5B,YAAY,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QACpC,YAAY,CAAC,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9D,YAAY,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAElC,MAAM,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACtD,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,MAAM,CAAC,QAAgB;QACnC,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,UAAU,cAAoB,QAAQ,CAAC,CAAC;QAE9E,OAAO,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtD,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,KAAK,CACjB,MAAe,EACf,KAAc;QAEd,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,KAAK,CACtD,SAAS,EACT,SAAS,EACT,SAAS,EACT,MAAM,EACN,KAAK,CACL,CAAC;QAEF,OAAO;YACN,OAAO,EAAE,MAAM,CAAC,QAAqB;YACrC,MAAM,EAAE,MAAM,CAAC,MAAM;SACrB,CAAC;IACH,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Guards } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { Tenant } from \"./entities/tenant.js\";\nimport type { ITenant } from \"./models/ITenant.js\";\nimport type { ITenantAdminComponent } from \"./models/ITenantAdminComponent.js\";\nimport type { ITenantAdminServiceConstructorOptions } from \"./models/ITenantAdminServiceConstructorOptions.js\";\n\n/**\n * Service for performing email messaging operations to a connector.\n */\nexport class TenantAdminService implements ITenantAdminComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TenantAdminService>();\n\n\t/**\n\t * Entity storage connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnector: IEntityStorageConnector<Tenant>;\n\n\t/**\n\t * Create a new instance of TenantAdminService.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: ITenantAdminServiceConstructorOptions) {\n\t\tthis._entityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\toptions?.tenantEntityStorageType ?? \"tenant\"\n\t\t);\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 TenantAdminService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Get a tenant by its id.\n\t * @param tenantId The id of the tenant.\n\t * @returns The tenant or undefined if not found.\n\t */\n\tpublic async get(tenantId: string): Promise<ITenant | undefined> {\n\t\tGuards.stringValue(TenantAdminService.CLASS_NAME, nameof(tenantId), tenantId);\n\n\t\tlet tenant;\n\n\t\ttry {\n\t\t\ttenant = await this._entityStorageConnector.get(tenantId);\n\t\t} catch {}\n\n\t\treturn tenant;\n\t}\n\n\t/**\n\t * Get a tenant by its api key.\n\t * @param apiKey The api key of the tenant.\n\t * @returns The tenant or undefined if not found.\n\t */\n\tpublic async getByApiKey(apiKey: string): Promise<ITenant | undefined> {\n\t\tGuards.stringValue(TenantAdminService.CLASS_NAME, nameof(apiKey), apiKey);\n\n\t\tlet tenant;\n\n\t\ttry {\n\t\t\ttenant = await this._entityStorageConnector.get(apiKey, \"apiKey\");\n\t\t} catch {}\n\n\t\treturn tenant;\n\t}\n\n\t/**\n\t * Set a tenant.\n\t * @param tenant The tenant to store.\n\t * @returns Nothing.\n\t */\n\tpublic async set(tenant: ITenant): Promise<void> {\n\t\tGuards.objectValue<ITenant>(TenantAdminService.CLASS_NAME, nameof(tenant), tenant);\n\n\t\tconst tenantEntity = new Tenant();\n\t\ttenantEntity.id = tenant.id;\n\t\ttenantEntity.apiKey = tenant.apiKey;\n\t\ttenantEntity.dateCreated = new Date(Date.now()).toISOString();\n\t\ttenantEntity.label = tenant.label;\n\n\t\tawait this._entityStorageConnector.set(tenantEntity);\n\t}\n\n\t/**\n\t * Remove a tenant by its id.\n\t * @param tenantId The id of the tenant.\n\t * @returns Nothing.\n\t */\n\tpublic async remove(tenantId: string): Promise<void> {\n\t\tGuards.stringValue(TenantAdminService.CLASS_NAME, nameof(tenantId), tenantId);\n\n\t\treturn this._entityStorageConnector.remove(tenantId);\n\t}\n\n\t/**\n\t * Query tenants with pagination.\n\t * @param cursor The cursor to start from.\n\t * @param limit The maximum number of tenants to return.\n\t * @returns The tenants and the next cursor if more tenants are available.\n\t */\n\tpublic async query(\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{ tenants: ITenant[]; cursor?: string }> {\n\t\tconst result = await this._entityStorageConnector.query(\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tcursor,\n\t\t\tlimit\n\t\t);\n\n\t\treturn {\n\t\t\ttenants: result.entities as ITenant[],\n\t\t\tcursor: result.cursor\n\t\t};\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tenantAdminService.js","sourceRoot":"","sources":["../../src/tenantAdminService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITenantAdminComponent, ITenant } from \"@twin.org/api-models\";\nimport { GeneralError, Guards, Is, Url, NotFoundError } from \"@twin.org/core\";\nimport { ComparisonOperator } from \"@twin.org/entity\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { Tenant } from \"./entities/tenant.js\";\nimport type { ITenantAdminServiceConstructorOptions } from \"./models/ITenantAdminServiceConstructorOptions.js\";\nimport { TenantIdHelper } from \"./utils/tenantIdHelper.js\";\n\n/**\n * Service for performing email messaging operations to a connector.\n */\nexport class TenantAdminService implements ITenantAdminComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TenantAdminService>();\n\n\t/**\n\t * Entity storage connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnector: IEntityStorageConnector<Tenant>;\n\n\t/**\n\t * Create a new instance of TenantAdminService.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: ITenantAdminServiceConstructorOptions) {\n\t\tthis._entityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\toptions?.tenantEntityStorageType ?? \"tenant\"\n\t\t);\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 TenantAdminService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Get a tenant by its id.\n\t * @param tenantId The id of the tenant.\n\t * @returns The tenant.\n\t * @throws Error if the tenant is not found.\n\t */\n\tpublic async get(tenantId: string): Promise<ITenant> {\n\t\tGuards.stringHexLength(TenantAdminService.CLASS_NAME, nameof(tenantId), tenantId, 32);\n\n\t\tlet tenant;\n\n\t\ttry {\n\t\t\ttenant = await this._entityStorageConnector.get(tenantId);\n\t\t} catch {}\n\n\t\tif (!Is.object(tenant)) {\n\t\t\tthrow new NotFoundError(TenantAdminService.CLASS_NAME, \"tenantNotFound\", tenantId);\n\t\t}\n\n\t\treturn tenant;\n\t}\n\n\t/**\n\t * Get a tenant by its api key.\n\t * @param apiKey The api key of the tenant.\n\t * @returns The tenant.\n\t * @throws Error if the tenant is not found.\n\t */\n\tpublic async getByApiKey(apiKey: string): Promise<ITenant> {\n\t\tGuards.stringHexLength(TenantAdminService.CLASS_NAME, nameof(apiKey), apiKey, 32);\n\n\t\tlet tenant;\n\n\t\ttry {\n\t\t\ttenant = await this._entityStorageConnector.get(apiKey, \"apiKey\");\n\t\t} catch {}\n\n\t\tif (!Is.object(tenant)) {\n\t\t\tthrow new NotFoundError(TenantAdminService.CLASS_NAME, \"tenantNotFound\", apiKey);\n\t\t}\n\n\t\treturn tenant;\n\t}\n\n\t/**\n\t * Get a tenant by its public origin.\n\t * @param publicOrigin The origin of the tenant.\n\t * @returns The tenant.\n\t * @throws Error if the tenant is not found.\n\t */\n\tpublic async getByPublicOrigin(publicOrigin: string): Promise<ITenant> {\n\t\tGuards.stringValue(TenantAdminService.CLASS_NAME, nameof(publicOrigin), publicOrigin);\n\n\t\tlet tenant;\n\n\t\ttry {\n\t\t\ttenant = await this._entityStorageConnector.get(publicOrigin, \"publicOrigin\");\n\t\t} catch {}\n\n\t\tif (!Is.object(tenant)) {\n\t\t\tthrow new NotFoundError(TenantAdminService.CLASS_NAME, \"tenantNotFound\", publicOrigin);\n\t\t}\n\n\t\treturn tenant;\n\t}\n\n\t/**\n\t * Create a tenant.\n\t * @param tenant The tenant to store.\n\t * @returns The tenant id.\n\t */\n\tpublic async create(\n\t\ttenant: Omit<ITenant, \"id\" | \"dateCreated\" | \"dateModified\"> & { id?: string }\n\t): Promise<string> {\n\t\tGuards.objectValue<ITenant>(TenantAdminService.CLASS_NAME, nameof(tenant), tenant);\n\t\tif (Is.stringValue(tenant.id)) {\n\t\t\tGuards.stringHexLength(TenantAdminService.CLASS_NAME, nameof(tenant.id), tenant.id, 32);\n\t\t}\n\t\tif (Is.stringValue(tenant.apiKey)) {\n\t\t\tGuards.stringHexLength(\n\t\t\t\tTenantAdminService.CLASS_NAME,\n\t\t\t\tnameof(tenant.apiKey),\n\t\t\t\ttenant.apiKey,\n\t\t\t\t32\n\t\t\t);\n\t\t}\n\n\t\tlet publicOrigin: string | undefined;\n\t\tif (Is.stringValue(tenant.publicOrigin)) {\n\t\t\tUrl.guard(TenantAdminService.CLASS_NAME, nameof(tenant.publicOrigin), tenant.publicOrigin);\n\n\t\t\tconst url = new Url(tenant.publicOrigin);\n\t\t\tconst parts = url.parts();\n\t\t\tpublicOrigin = `${parts.schema}://${parts.host}${Is.integer(parts.port) ? `:${parts.port}` : \"\"}`;\n\t\t}\n\n\t\tif (Is.stringValue(tenant.apiKey)) {\n\t\t\tconst existingApiKey = await this._entityStorageConnector.get(tenant.apiKey, \"apiKey\");\n\t\t\tif (Is.object(existingApiKey) && existingApiKey.id !== tenant.id) {\n\t\t\t\tthrow new GeneralError(TenantAdminService.CLASS_NAME, \"apiKeyAlreadyInUse\");\n\t\t\t}\n\t\t}\n\n\t\tconst tenantEntity = new Tenant();\n\t\ttenantEntity.id = tenant.id ?? TenantIdHelper.generateTenantId();\n\t\ttenantEntity.apiKey = tenant.apiKey ?? TenantIdHelper.generateApiKey();\n\t\ttenantEntity.dateCreated = new Date(Date.now()).toISOString();\n\t\ttenantEntity.dateModified = tenantEntity.dateCreated;\n\t\ttenantEntity.label = tenant.label;\n\t\ttenantEntity.publicOrigin = publicOrigin;\n\t\ttenantEntity.isNodeTenant = tenant.isNodeTenant;\n\n\t\tawait this._entityStorageConnector.set(tenantEntity);\n\n\t\treturn tenantEntity.id;\n\t}\n\n\t/**\n\t * Update a tenant.\n\t * @param tenant The tenant to update.\n\t * @returns The nothing.\n\t */\n\tpublic async update(\n\t\ttenant: Partial<Omit<ITenant, \"dateCreated\" | \"dateModified\">>\n\t): Promise<void> {\n\t\tGuards.objectValue<ITenant>(TenantAdminService.CLASS_NAME, nameof(tenant), tenant);\n\t\tGuards.stringHexLength(TenantAdminService.CLASS_NAME, nameof(tenant.id), tenant.id, 32);\n\t\tif (Is.stringValue(tenant.apiKey)) {\n\t\t\tGuards.stringHexLength(\n\t\t\t\tTenantAdminService.CLASS_NAME,\n\t\t\t\tnameof(tenant.apiKey),\n\t\t\t\ttenant.apiKey,\n\t\t\t\t32\n\t\t\t);\n\t\t}\n\n\t\tlet publicOrigin: string | undefined;\n\t\tif (Is.stringValue(tenant.publicOrigin)) {\n\t\t\tUrl.guard(TenantAdminService.CLASS_NAME, nameof(tenant.publicOrigin), tenant.publicOrigin);\n\n\t\t\tconst url = new Url(tenant.publicOrigin);\n\t\t\tconst parts = url.parts();\n\t\t\tpublicOrigin = `${parts.schema}://${parts.host}${Is.integer(parts.port) ? `:${parts.port}` : \"\"}`;\n\t\t}\n\n\t\tconst currentTenant = await this._entityStorageConnector.get(tenant.id);\n\t\tif (!Is.object(currentTenant)) {\n\t\t\tthrow new NotFoundError(TenantAdminService.CLASS_NAME, \"tenantNotFound\", tenant.id);\n\t\t}\n\n\t\tif (Is.stringValue(tenant.apiKey)) {\n\t\t\tconst existingApiKey = await this._entityStorageConnector.get(tenant.apiKey, \"apiKey\");\n\t\t\tif (Is.object(existingApiKey) && existingApiKey.id !== currentTenant.id) {\n\t\t\t\tthrow new GeneralError(TenantAdminService.CLASS_NAME, \"apiKeyAlreadyInUse\");\n\t\t\t}\n\t\t}\n\n\t\tconst tenantEntity = new Tenant();\n\t\ttenantEntity.id = tenant.id;\n\t\ttenantEntity.apiKey = tenant.apiKey ?? currentTenant.apiKey;\n\t\ttenantEntity.dateCreated = currentTenant.dateCreated;\n\t\ttenantEntity.dateModified = new Date(Date.now()).toISOString();\n\t\ttenantEntity.label = tenant.label ?? currentTenant.label;\n\t\ttenantEntity.publicOrigin = publicOrigin ?? currentTenant.publicOrigin;\n\t\ttenantEntity.isNodeTenant = tenant.isNodeTenant ?? currentTenant.isNodeTenant;\n\n\t\tawait this._entityStorageConnector.set(tenantEntity);\n\t}\n\n\t/**\n\t * Remove a tenant by its id.\n\t * @param tenantId The id of the tenant.\n\t * @returns Nothing.\n\t */\n\tpublic async remove(tenantId: string): Promise<void> {\n\t\tGuards.stringHexLength(TenantAdminService.CLASS_NAME, nameof(tenantId), tenantId, 32);\n\n\t\treturn this._entityStorageConnector.remove(tenantId);\n\t}\n\n\t/**\n\t * Query tenants with pagination.\n\t * @param options Optional query options.\n\t * @param options.isNodeTenant Whether to filter for node admin tenants.\n\t * @param cursor The cursor to start from.\n\t * @param limit The maximum number of tenants to return.\n\t * @returns The tenants and the next cursor if more tenants are available.\n\t */\n\tpublic async query(\n\t\toptions?: { isNodeTenant?: boolean },\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{ tenants: ITenant[]; cursor?: string }> {\n\t\tconst conditions = [];\n\n\t\tif (Is.boolean(options?.isNodeTenant)) {\n\t\t\tconditions.push({\n\t\t\t\tproperty: \"isNodeTenant\",\n\t\t\t\tvalue: options.isNodeTenant,\n\t\t\t\tcomparison: ComparisonOperator.Equals\n\t\t\t});\n\t\t}\n\n\t\tconst result = await this._entityStorageConnector.query(\n\t\t\tconditions.length > 0 ? { conditions } : undefined,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tcursor,\n\t\t\tlimit\n\t\t);\n\n\t\treturn {\n\t\t\ttenants: result.entities as ITenant[],\n\t\t\tcursor: result.cursor\n\t\t};\n\t}\n}\n"]}
|
|
@@ -7,13 +7,20 @@ export class TenantIdContextIdHandler {
|
|
|
7
7
|
* Runtime name for the class.
|
|
8
8
|
*/
|
|
9
9
|
static CLASS_NAME = "TenantIdContextIdHandler";
|
|
10
|
+
/**
|
|
11
|
+
* The class name of the component.
|
|
12
|
+
* @returns The class name.
|
|
13
|
+
*/
|
|
14
|
+
className() {
|
|
15
|
+
return TenantIdContextIdHandler.CLASS_NAME;
|
|
16
|
+
}
|
|
10
17
|
/**
|
|
11
18
|
* The short form of the tenant id is the base64 version to compact.
|
|
12
19
|
* @param value The full context id value.
|
|
13
20
|
* @returns Short form string.
|
|
14
21
|
*/
|
|
15
22
|
short(value) {
|
|
16
|
-
return Converter.
|
|
23
|
+
return Converter.bytesToBase64Url(Converter.hexToBytes(value));
|
|
17
24
|
}
|
|
18
25
|
/**
|
|
19
26
|
* Guard the value ensuring length.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenantIdContextIdHandler.js","sourceRoot":"","sources":["../../src/tenantIdContextIdHandler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGnD;;GAEG;AACH,MAAM,OAAO,wBAAwB;IACpC;;OAEG;IACI,MAAM,CAAU,UAAU,8BAA8C;IAE/E;;;;OAIG;IACI,KAAK,CAAC,KAAa;QACzB,OAAO,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"tenantIdContextIdHandler.js","sourceRoot":"","sources":["../../src/tenantIdContextIdHandler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGnD;;GAEG;AACH,MAAM,OAAO,wBAAwB;IACpC;;OAEG;IACI,MAAM,CAAU,UAAU,8BAA8C;IAE/E;;;OAGG;IACI,SAAS;QACf,OAAO,wBAAwB,CAAC,UAAU,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,KAAa;QACzB,OAAO,SAAS,CAAC,gBAAgB,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAChE,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,KAAa;QACzB,MAAM,CAAC,eAAe,CAAC,wBAAwB,CAAC,UAAU,WAAiB,KAAK,EAAE,EAAE,CAAC,CAAC;IACvF,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IContextIdHandler } from \"@twin.org/context\";\nimport { Converter, Guards } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\n\n/**\n * Context Id handler for testing as a tenant id.\n */\nexport class TenantIdContextIdHandler implements IContextIdHandler {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TenantIdContextIdHandler>();\n\n\t/**\n\t * The class name of the component.\n\t * @returns The class name.\n\t */\n\tpublic className(): string {\n\t\treturn TenantIdContextIdHandler.CLASS_NAME;\n\t}\n\n\t/**\n\t * The short form of the tenant id is the base64 version to compact.\n\t * @param value The full context id value.\n\t * @returns Short form string.\n\t */\n\tpublic short(value: string): string {\n\t\treturn Converter.bytesToBase64Url(Converter.hexToBytes(value));\n\t}\n\n\t/**\n\t * Guard the value ensuring length.\n\t * @param value The value to guard.\n\t * @throws GeneralError if the value is too short.\n\t */\n\tpublic guard(value: string): void {\n\t\tGuards.stringHexLength(TenantIdContextIdHandler.CLASS_NAME, nameof(value), value, 32);\n\t}\n}\n"]}
|
|
@@ -52,31 +52,36 @@ export class TenantProcessor {
|
|
|
52
52
|
* @param processorState The state handed through the processors.
|
|
53
53
|
*/
|
|
54
54
|
async pre(request, response, route, contextIds, processorState) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
if (!Is.empty(route) && !(route.skipTenant ?? false)) {
|
|
56
|
+
const apiKey = request.headers?.[this._apiKeyName] ?? request.query?.[this._apiKeyName];
|
|
57
|
+
let errorResponse;
|
|
58
|
+
if (Is.stringValue(apiKey)) {
|
|
59
|
+
try {
|
|
60
|
+
const nodeTenant = await this._entityStorageConnector.get(apiKey, "apiKey");
|
|
61
|
+
if (Is.empty(nodeTenant)) {
|
|
62
|
+
errorResponse = new UnauthorizedError(TenantProcessor.CLASS_NAME, "apiKeyNotFound", {
|
|
63
|
+
key: apiKey
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
contextIds[ContextIdKeys.Tenant] = nodeTenant.id;
|
|
68
|
+
if (Is.stringValue(nodeTenant.publicOrigin)) {
|
|
69
|
+
processorState.publicOrigin = nodeTenant.publicOrigin;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
64
72
|
}
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
catch (err) {
|
|
74
|
+
errorResponse = BaseError.fromError(err);
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
|
-
|
|
70
|
-
errorResponse =
|
|
77
|
+
else {
|
|
78
|
+
errorResponse = new UnauthorizedError(TenantProcessor.CLASS_NAME, "missingApiKey", {
|
|
79
|
+
keyName: this._apiKeyName
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (!Is.empty(errorResponse)) {
|
|
83
|
+
HttpErrorHelper.buildResponse(response, errorResponse, HttpStatusCode.unauthorized);
|
|
71
84
|
}
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
errorResponse = new UnauthorizedError(TenantProcessor.CLASS_NAME, "missingApiKey", {
|
|
75
|
-
keyName: this._apiKeyName
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
if (!Is.empty(errorResponse)) {
|
|
79
|
-
HttpErrorHelper.buildResponse(response, errorResponse, HttpStatusCode.unauthorized);
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenantProcessor.js","sourceRoot":"","sources":["../../src/tenantProcessor.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EACN,eAAe,EAKf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAoB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,SAAS,EAAe,EAAE,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EACN,6BAA6B,EAE7B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAI/C;;GAEG;AACH,MAAM,OAAO,eAAe;IAC3B;;;OAGG;IACI,MAAM,CAAU,oBAAoB,GAAW,WAAW,CAAC;IAElE;;OAEG;IACI,MAAM,CAAU,UAAU,qBAAqC;IAEtE;;;OAGG;IACc,uBAAuB,CAAkC;IAE1E;;;OAGG;IACc,WAAW,CAAS;IAErC;;;OAGG;IACH,YAAY,OAA4C;QACvD,IAAI,CAAC,uBAAuB,GAAG,6BAA6B,CAAC,GAAG,CAC/D,OAAO,EAAE,uBAAuB,IAAI,QAAQ,CAC5C,CAAC;QACF,IAAI,CAAC,WAAW,GAAG,OAAO,EAAE,MAAM,EAAE,UAAU,IAAI,eAAe,CAAC,oBAAoB,CAAC;IACxF,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,eAAe,CAAC,UAAU,CAAC;IACnC,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,GAAG,CACf,OAA2B,EAC3B,QAAuB,EACvB,KAA6B,EAC7B,UAAuB,EACvB,cAAyC;QAEzC,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"tenantProcessor.js","sourceRoot":"","sources":["../../src/tenantProcessor.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EACN,eAAe,EAKf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAoB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,SAAS,EAAe,EAAE,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EACN,6BAA6B,EAE7B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAI/C;;GAEG;AACH,MAAM,OAAO,eAAe;IAC3B;;;OAGG;IACI,MAAM,CAAU,oBAAoB,GAAW,WAAW,CAAC;IAElE;;OAEG;IACI,MAAM,CAAU,UAAU,qBAAqC;IAEtE;;;OAGG;IACc,uBAAuB,CAAkC;IAE1E;;;OAGG;IACc,WAAW,CAAS;IAErC;;;OAGG;IACH,YAAY,OAA4C;QACvD,IAAI,CAAC,uBAAuB,GAAG,6BAA6B,CAAC,GAAG,CAC/D,OAAO,EAAE,uBAAuB,IAAI,QAAQ,CAC5C,CAAC;QACF,IAAI,CAAC,WAAW,GAAG,OAAO,EAAE,MAAM,EAAE,UAAU,IAAI,eAAe,CAAC,oBAAoB,CAAC;IACxF,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,eAAe,CAAC,UAAU,CAAC;IACnC,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,GAAG,CACf,OAA2B,EAC3B,QAAuB,EACvB,KAA6B,EAC7B,UAAuB,EACvB,cAAyC;QAEzC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,EAAE,CAAC;YACtD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACxF,IAAI,aAAiC,CAAC;YAEtC,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5B,IAAI,CAAC;oBACJ,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;oBAE5E,IAAI,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC1B,aAAa,GAAG,IAAI,iBAAiB,CAAC,eAAe,CAAC,UAAU,EAAE,gBAAgB,EAAE;4BACnF,GAAG,EAAE,MAAM;yBACX,CAAC,CAAC;oBACJ,CAAC;yBAAM,CAAC;wBACP,UAAU,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,EAAE,CAAC;wBACjD,IAAI,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;4BAC7C,cAAc,CAAC,YAAY,GAAG,UAAU,CAAC,YAAY,CAAC;wBACvD,CAAC;oBACF,CAAC;gBACF,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACd,aAAa,GAAG,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBAC1C,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,aAAa,GAAG,IAAI,iBAAiB,CAAC,eAAe,CAAC,UAAU,EAAE,eAAe,EAAE;oBAClF,OAAO,EAAE,IAAI,CAAC,WAAW;iBACzB,CAAC,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC9B,eAAe,CAAC,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;YACrF,CAAC;QACF,CAAC;IACF,CAAC","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} from \"@twin.org/api-models\";\nimport { ContextIdKeys, type IContextIds } from \"@twin.org/context\";\nimport { BaseError, type IError, 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 entity storage for api keys.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnector: IEntityStorageConnector<Tenant>;\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 * Create a new instance of NodeTenantProcessor.\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._apiKeyName = options?.config?.apiKeyName ?? TenantProcessor.DEFAULT_API_KEY_NAME;\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\tconst apiKey = request.headers?.[this._apiKeyName] ?? request.query?.[this._apiKeyName];\n\t\t\tlet errorResponse: IError | undefined;\n\n\t\t\tif (Is.stringValue(apiKey)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst nodeTenant = await this._entityStorageConnector.get(apiKey, \"apiKey\");\n\n\t\t\t\t\tif (Is.empty(nodeTenant)) {\n\t\t\t\t\t\terrorResponse = new UnauthorizedError(TenantProcessor.CLASS_NAME, \"apiKeyNotFound\", {\n\t\t\t\t\t\t\tkey: apiKey\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontextIds[ContextIdKeys.Tenant] = nodeTenant.id;\n\t\t\t\t\t\tif (Is.stringValue(nodeTenant.publicOrigin)) {\n\t\t\t\t\t\t\tprocessorState.publicOrigin = nodeTenant.publicOrigin;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\terrorResponse = BaseError.fromError(err);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\terrorResponse = new UnauthorizedError(TenantProcessor.CLASS_NAME, \"missingApiKey\", {\n\t\t\t\t\tkeyName: this._apiKeyName\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!Is.empty(errorResponse)) {\n\t\t\t\tHttpErrorHelper.buildResponse(response, errorResponse, HttpStatusCode.unauthorized);\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|