@openhi/constructs 0.0.159 → 0.0.161
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/lib/{chunk-HQ67J7BP.mjs → chunk-5S6VFBLT.mjs} +12 -70
- package/lib/chunk-5S6VFBLT.mjs.map +1 -0
- package/lib/{chunk-MVQWAIMC.mjs → chunk-6BB4CRSS.mjs} +3 -312
- package/lib/chunk-6BB4CRSS.mjs.map +1 -0
- package/lib/{chunk-WPCBVDFZ.mjs → chunk-76UM2LQ5.mjs} +2 -2
- package/lib/chunk-7TRO2STL.mjs +4616 -0
- package/lib/chunk-7TRO2STL.mjs.map +1 -0
- package/lib/chunk-BUAYVN3C.mjs +87 -0
- package/lib/chunk-BUAYVN3C.mjs.map +1 -0
- package/lib/{chunk-23PUSHBV.mjs → chunk-D2Y6DDOC.mjs} +2 -2
- package/lib/chunk-DWSWCUZR.mjs +123 -0
- package/lib/chunk-DWSWCUZR.mjs.map +1 -0
- package/lib/{chunk-VZCPGQXA.mjs → chunk-EUIP2U5F.mjs} +69 -1
- package/lib/{chunk-VZCPGQXA.mjs.map → chunk-EUIP2U5F.mjs.map} +1 -1
- package/lib/chunk-GJTPXJKD.mjs +46 -0
- package/lib/chunk-GJTPXJKD.mjs.map +1 -0
- package/lib/chunk-I6LUPJUY.mjs +61 -0
- package/lib/chunk-I6LUPJUY.mjs.map +1 -0
- package/lib/{chunk-KR2Y2CVQ.mjs → chunk-KA3OMP3X.mjs} +2 -2
- package/lib/{chunk-ZM4GDHHC.mjs → chunk-KMEWULMX.mjs} +51 -3
- package/lib/chunk-KMEWULMX.mjs.map +1 -0
- package/lib/chunk-LKKLO66E.mjs +25 -0
- package/lib/chunk-LKKLO66E.mjs.map +1 -0
- package/lib/{chunk-CFJDATDK.mjs → chunk-MLFMW5IF.mjs} +43 -9
- package/lib/chunk-MLFMW5IF.mjs.map +1 -0
- package/lib/chunk-O5VQWB6U.mjs +315 -0
- package/lib/chunk-O5VQWB6U.mjs.map +1 -0
- package/lib/{chunk-7BQHLC7U.mjs → chunk-P3CTZWC2.mjs} +8 -40
- package/lib/chunk-P3CTZWC2.mjs.map +1 -0
- package/lib/chunk-P3NFCKTZ.mjs +502 -0
- package/lib/chunk-P3NFCKTZ.mjs.map +1 -0
- package/lib/{chunk-M7Y3BOQW.mjs → chunk-Q3MKITPY.mjs} +5 -5
- package/lib/chunk-Q64MOYJ7.mjs +218 -0
- package/lib/chunk-Q64MOYJ7.mjs.map +1 -0
- package/lib/chunk-RQKJNMX5.mjs +89 -0
- package/lib/chunk-RQKJNMX5.mjs.map +1 -0
- package/lib/{chunk-ZWSGM6PZ.mjs → chunk-SD7J3N3C.mjs} +2 -2
- package/lib/{chunk-7RZHFI77.mjs → chunk-VESULYQQ.mjs} +2 -2
- package/lib/{chunk-AOSEKL7U.mjs → chunk-WOTU36P3.mjs} +6 -103
- package/lib/chunk-WOTU36P3.mjs.map +1 -0
- package/lib/{chunk-X5E4YJGZ.mjs → chunk-YPTJJ35S.mjs} +2 -2
- package/lib/counter-apply-operation-DZM3MIDm.d.mts +63 -0
- package/lib/counter-apply-operation-DZM3MIDm.d.ts +63 -0
- package/lib/counter-maintenance.handler.d.mts +38 -0
- package/lib/counter-maintenance.handler.d.ts +38 -0
- package/lib/counter-maintenance.handler.js +2885 -0
- package/lib/counter-maintenance.handler.js.map +1 -0
- package/lib/counter-maintenance.handler.mjs +180 -0
- package/lib/counter-maintenance.handler.mjs.map +1 -0
- package/lib/counter-reconciliation.handler.d.mts +116 -0
- package/lib/counter-reconciliation.handler.d.ts +116 -0
- package/lib/counter-reconciliation.handler.js +3324 -0
- package/lib/counter-reconciliation.handler.js.map +1 -0
- package/lib/counter-reconciliation.handler.mjs +295 -0
- package/lib/counter-reconciliation.handler.mjs.map +1 -0
- package/lib/data-store-postgres-replication.handler.js +50 -2
- package/lib/data-store-postgres-replication.handler.js.map +1 -1
- package/lib/data-store-postgres-replication.handler.mjs +2 -2
- package/lib/delete-chunk.handler.js +118 -2
- package/lib/delete-chunk.handler.js.map +1 -1
- package/lib/delete-chunk.handler.mjs +3 -3
- package/lib/{events-DTgo2dcW.d.mts → events-TG654e7L.d.mts} +68 -19
- package/lib/{events-DTgo2dcW.d.ts → events-TG654e7L.d.ts} +68 -19
- package/lib/finalize.handler.js +50 -2
- package/lib/finalize.handler.js.map +1 -1
- package/lib/finalize.handler.mjs +4 -4
- package/lib/firehose-archive-transform.handler.js +50 -2
- package/lib/firehose-archive-transform.handler.js.map +1 -1
- package/lib/firehose-archive-transform.handler.mjs +2 -2
- package/lib/index.d.mts +1283 -4
- package/lib/index.d.ts +1389 -24
- package/lib/index.js +4113 -320
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +602 -195
- package/lib/index.mjs.map +1 -1
- package/lib/list-chunks.handler.js +118 -2
- package/lib/list-chunks.handler.js.map +1 -1
- package/lib/list-chunks.handler.mjs +3 -3
- package/lib/platform-deploy-bridge.handler.js +50 -2
- package/lib/platform-deploy-bridge.handler.js.map +1 -1
- package/lib/platform-deploy-bridge.handler.mjs +1 -1
- package/lib/pre-token-generation.handler.js +68 -0
- package/lib/pre-token-generation.handler.js.map +1 -1
- package/lib/pre-token-generation.handler.mjs +9 -5
- package/lib/pre-token-generation.handler.mjs.map +1 -1
- package/lib/provision-default-workspace.handler.js +887 -4
- package/lib/provision-default-workspace.handler.js.map +1 -1
- package/lib/provision-default-workspace.handler.mjs +14 -9
- package/lib/provision-default-workspace.handler.mjs.map +1 -1
- package/lib/rename-finalize.handler.js +50 -2
- package/lib/rename-finalize.handler.js.map +1 -1
- package/lib/rename-finalize.handler.mjs +2 -2
- package/lib/rename-list-targets.handler.js +118 -2
- package/lib/rename-list-targets.handler.js.map +1 -1
- package/lib/rename-list-targets.handler.mjs +11 -9
- package/lib/rename-list-targets.handler.mjs.map +1 -1
- package/lib/rename-rewrite-chunk.handler.js +68 -0
- package/lib/rename-rewrite-chunk.handler.js.map +1 -1
- package/lib/rename-rewrite-chunk.handler.mjs +2 -2
- package/lib/rest-api-lambda.handler.js +1454 -251
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +673 -821
- package/lib/rest-api-lambda.handler.mjs.map +1 -1
- package/lib/seed-demo-data.handler.d.mts +1 -1
- package/lib/seed-demo-data.handler.d.ts +1 -1
- package/lib/seed-demo-data.handler.js +4004 -201
- package/lib/seed-demo-data.handler.js.map +1 -1
- package/lib/seed-demo-data.handler.mjs +10 -7
- package/lib/seed-system-data.handler.js +118 -2
- package/lib/seed-system-data.handler.js.map +1 -1
- package/lib/seed-system-data.handler.mjs +5 -5
- package/package.json +1 -1
- package/lib/chunk-7BQHLC7U.mjs.map +0 -1
- package/lib/chunk-AOSEKL7U.mjs.map +0 -1
- package/lib/chunk-BQMJSDOD.mjs +0 -1136
- package/lib/chunk-BQMJSDOD.mjs.map +0 -1
- package/lib/chunk-CFJDATDK.mjs.map +0 -1
- package/lib/chunk-E6MCKJVS.mjs +0 -212
- package/lib/chunk-E6MCKJVS.mjs.map +0 -1
- package/lib/chunk-HQ67J7BP.mjs.map +0 -1
- package/lib/chunk-MVQWAIMC.mjs.map +0 -1
- package/lib/chunk-ZM4GDHHC.mjs.map +0 -1
- /package/lib/{chunk-WPCBVDFZ.mjs.map → chunk-76UM2LQ5.mjs.map} +0 -0
- /package/lib/{chunk-23PUSHBV.mjs.map → chunk-D2Y6DDOC.mjs.map} +0 -0
- /package/lib/{chunk-KR2Y2CVQ.mjs.map → chunk-KA3OMP3X.mjs.map} +0 -0
- /package/lib/{chunk-M7Y3BOQW.mjs.map → chunk-Q3MKITPY.mjs.map} +0 -0
- /package/lib/{chunk-ZWSGM6PZ.mjs.map → chunk-SD7J3N3C.mjs.map} +0 -0
- /package/lib/{chunk-7RZHFI77.mjs.map → chunk-VESULYQQ.mjs.map} +0 -0
- /package/lib/{chunk-X5E4YJGZ.mjs.map → chunk-YPTJJ35S.mjs.map} +0 -0
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,6 +30,727 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
33
|
+
// ../workflows/lib/envelope-version.js
|
|
34
|
+
var require_envelope_version = __commonJS({
|
|
35
|
+
"../workflows/lib/envelope-version.js"(exports2) {
|
|
36
|
+
"use strict";
|
|
37
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
38
|
+
exports2.ENVELOPE_VERSION = void 0;
|
|
39
|
+
exports2.isSupportedEnvelopeVersion = isSupportedEnvelopeVersion;
|
|
40
|
+
exports2.ENVELOPE_VERSION = "1.0";
|
|
41
|
+
var ENVELOPE_VERSION_PATTERN = /^\d+\.\d+$/;
|
|
42
|
+
var MIN_SUPPORTED_MAJOR = 1;
|
|
43
|
+
var MAX_SUPPORTED_MAJOR = 1;
|
|
44
|
+
function isSupportedEnvelopeVersion(version) {
|
|
45
|
+
if (!ENVELOPE_VERSION_PATTERN.test(version)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const major = Number.parseInt(version.split(".")[0], 10);
|
|
49
|
+
return major >= MIN_SUPPORTED_MAJOR && major <= MAX_SUPPORTED_MAJOR;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ../workflows/lib/envelope.js
|
|
55
|
+
var require_envelope = __commonJS({
|
|
56
|
+
"../workflows/lib/envelope.js"(exports2) {
|
|
57
|
+
"use strict";
|
|
58
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
59
|
+
exports2.MissingActorContextError = void 0;
|
|
60
|
+
exports2.isWorkflowUserActor = isWorkflowUserActor;
|
|
61
|
+
exports2.isWorkflowSystemActor = isWorkflowSystemActor;
|
|
62
|
+
exports2.workflowUserActorFromClaims = workflowUserActorFromClaims;
|
|
63
|
+
function isWorkflowUserActor(actor) {
|
|
64
|
+
return actor.ohi_uid !== void 0;
|
|
65
|
+
}
|
|
66
|
+
function isWorkflowSystemActor(actor) {
|
|
67
|
+
return actor.system !== void 0;
|
|
68
|
+
}
|
|
69
|
+
function workflowUserActorFromClaims(claims) {
|
|
70
|
+
if (claims.ohi_tid === void 0 || claims.ohi_wid === void 0) {
|
|
71
|
+
throw new MissingActorContextError("workflowUserActorFromClaims: ohi_tid and ohi_wid are required on the workflow user-actor; the caller's JWT is missing one or both. Use a system-actor for pre-provisioning bootstrap workflows.");
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
ohi_tid: claims.ohi_tid,
|
|
75
|
+
ohi_wid: claims.ohi_wid,
|
|
76
|
+
ohi_uid: claims.ohi_uid,
|
|
77
|
+
ohi_uname: claims.ohi_uname
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
var MissingActorContextError = class extends Error {
|
|
81
|
+
/** @param message - human-readable description of the missing claim. */
|
|
82
|
+
constructor(message) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.name = "MissingActorContextError";
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
exports2.MissingActorContextError = MissingActorContextError;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// ../workflows/lib/sources.js
|
|
92
|
+
var require_sources = __commonJS({
|
|
93
|
+
"../workflows/lib/sources.js"(exports2) {
|
|
94
|
+
"use strict";
|
|
95
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
96
|
+
exports2.DEFAULT_BUS_NAME_BY_SOURCE = exports2.OPENHI_OPS_SOURCE = exports2.OPENHI_DATA_SOURCE = exports2.OPENHI_CONTROL_SOURCE = void 0;
|
|
97
|
+
exports2.OPENHI_CONTROL_SOURCE = "openhi.control";
|
|
98
|
+
exports2.OPENHI_DATA_SOURCE = "openhi.data";
|
|
99
|
+
exports2.OPENHI_OPS_SOURCE = "openhi.ops";
|
|
100
|
+
exports2.DEFAULT_BUS_NAME_BY_SOURCE = {
|
|
101
|
+
[exports2.OPENHI_CONTROL_SOURCE]: "openhi-control-event-bus",
|
|
102
|
+
[exports2.OPENHI_DATA_SOURCE]: "openhi-data-event-bus",
|
|
103
|
+
[exports2.OPENHI_OPS_SOURCE]: "openhi-ops-event-bus"
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ../workflows/lib/detail-types/registry.js
|
|
109
|
+
var require_registry = __commonJS({
|
|
110
|
+
"../workflows/lib/detail-types/registry.js"(exports2) {
|
|
111
|
+
"use strict";
|
|
112
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
113
|
+
exports2.InvalidDetailTypeRegistrationError = void 0;
|
|
114
|
+
exports2.defineDetailType = defineDetailType;
|
|
115
|
+
exports2.isWellFormedDetailType = isWellFormedDetailType;
|
|
116
|
+
function defineDetailType(entry) {
|
|
117
|
+
if (!isWellFormedDetailType(entry.detailType)) {
|
|
118
|
+
throw new InvalidDetailTypeRegistrationError(`Detail-type "${entry.detailType}" does not match the platform-wide format <area>.<event>.v<integer>. See TR-016 \xA7Open Items #2.`);
|
|
119
|
+
}
|
|
120
|
+
return entry;
|
|
121
|
+
}
|
|
122
|
+
var DETAIL_TYPE_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z0-9]+(?:-[a-z0-9]+)*\.v\d+$/;
|
|
123
|
+
function isWellFormedDetailType(detailType) {
|
|
124
|
+
return DETAIL_TYPE_PATTERN.test(detailType);
|
|
125
|
+
}
|
|
126
|
+
var InvalidDetailTypeRegistrationError = class extends Error {
|
|
127
|
+
/** @param message - human-readable description of the violation. */
|
|
128
|
+
constructor(message) {
|
|
129
|
+
super(message);
|
|
130
|
+
this.name = "InvalidDetailTypeRegistrationError";
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
exports2.InvalidDetailTypeRegistrationError = InvalidDetailTypeRegistrationError;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// ../workflows/lib/detail-types/control-plane.js
|
|
138
|
+
var require_control_plane = __commonJS({
|
|
139
|
+
"../workflows/lib/detail-types/control-plane.js"(exports2) {
|
|
140
|
+
"use strict";
|
|
141
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
142
|
+
exports2.ControlPlaneWorkspaceDeletedV1 = exports2.ControlPlaneWorkspaceCreatedV1 = exports2.ControlPlaneRoleAssignmentDeletedV1 = exports2.ControlPlaneRoleAssignmentCreatedV1 = exports2.ControlPlaneMembershipDeletedV1 = exports2.ControlPlaneMembershipCreatedV1 = exports2.ControlPlaneRenameFailedV1 = exports2.ControlPlaneRenameCompleteV1 = exports2.ControlPlaneRenameV1 = exports2.RENAMABLE_ENTITY_TYPE = exports2.ControlPlaneOwningDeleteFailedV1 = exports2.ControlPlaneOwningDeleteCompleteV1 = exports2.ControlPlaneOwningDeleteV1 = exports2.OWNING_ENTITY_TYPE = void 0;
|
|
143
|
+
var sources_1 = require_sources();
|
|
144
|
+
var registry_1 = require_registry();
|
|
145
|
+
exports2.OWNING_ENTITY_TYPE = {
|
|
146
|
+
Workspace: "Workspace",
|
|
147
|
+
User: "User"
|
|
148
|
+
};
|
|
149
|
+
exports2.ControlPlaneOwningDeleteV1 = (0, registry_1.defineDetailType)({
|
|
150
|
+
detailType: "control-plane.owning-delete.v1",
|
|
151
|
+
source: sources_1.OPENHI_DATA_SOURCE,
|
|
152
|
+
dedupRequired: true
|
|
153
|
+
});
|
|
154
|
+
exports2.ControlPlaneOwningDeleteCompleteV1 = (0, registry_1.defineDetailType)({
|
|
155
|
+
detailType: "control-plane.owning-delete-complete.v1",
|
|
156
|
+
source: sources_1.OPENHI_OPS_SOURCE,
|
|
157
|
+
dedupRequired: true
|
|
158
|
+
});
|
|
159
|
+
exports2.ControlPlaneOwningDeleteFailedV1 = (0, registry_1.defineDetailType)({
|
|
160
|
+
detailType: "control-plane.owning-delete-failed.v1",
|
|
161
|
+
source: sources_1.OPENHI_OPS_SOURCE,
|
|
162
|
+
dedupRequired: true
|
|
163
|
+
});
|
|
164
|
+
exports2.RENAMABLE_ENTITY_TYPE = {
|
|
165
|
+
Tenant: "Tenant",
|
|
166
|
+
User: "User",
|
|
167
|
+
Role: "Role"
|
|
168
|
+
};
|
|
169
|
+
exports2.ControlPlaneRenameV1 = (0, registry_1.defineDetailType)({
|
|
170
|
+
detailType: "control-plane.rename.v1",
|
|
171
|
+
source: sources_1.OPENHI_DATA_SOURCE,
|
|
172
|
+
dedupRequired: true
|
|
173
|
+
});
|
|
174
|
+
exports2.ControlPlaneRenameCompleteV1 = (0, registry_1.defineDetailType)({
|
|
175
|
+
detailType: "control-plane.rename-complete.v1",
|
|
176
|
+
source: sources_1.OPENHI_OPS_SOURCE,
|
|
177
|
+
dedupRequired: true
|
|
178
|
+
});
|
|
179
|
+
exports2.ControlPlaneRenameFailedV1 = (0, registry_1.defineDetailType)({
|
|
180
|
+
detailType: "control-plane.rename-failed.v1",
|
|
181
|
+
source: sources_1.OPENHI_OPS_SOURCE,
|
|
182
|
+
dedupRequired: true
|
|
183
|
+
});
|
|
184
|
+
exports2.ControlPlaneMembershipCreatedV1 = (0, registry_1.defineDetailType)({
|
|
185
|
+
detailType: "control-plane.membership-created.v1",
|
|
186
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
187
|
+
dedupRequired: true
|
|
188
|
+
});
|
|
189
|
+
exports2.ControlPlaneMembershipDeletedV1 = (0, registry_1.defineDetailType)({
|
|
190
|
+
detailType: "control-plane.membership-deleted.v1",
|
|
191
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
192
|
+
dedupRequired: true
|
|
193
|
+
});
|
|
194
|
+
exports2.ControlPlaneRoleAssignmentCreatedV1 = (0, registry_1.defineDetailType)({
|
|
195
|
+
detailType: "control-plane.role-assignment-created.v1",
|
|
196
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
197
|
+
dedupRequired: true
|
|
198
|
+
});
|
|
199
|
+
exports2.ControlPlaneRoleAssignmentDeletedV1 = (0, registry_1.defineDetailType)({
|
|
200
|
+
detailType: "control-plane.role-assignment-deleted.v1",
|
|
201
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
202
|
+
dedupRequired: true
|
|
203
|
+
});
|
|
204
|
+
exports2.ControlPlaneWorkspaceCreatedV1 = (0, registry_1.defineDetailType)({
|
|
205
|
+
detailType: "control-plane.workspace-created.v1",
|
|
206
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
207
|
+
dedupRequired: true
|
|
208
|
+
});
|
|
209
|
+
exports2.ControlPlaneWorkspaceDeletedV1 = (0, registry_1.defineDetailType)({
|
|
210
|
+
detailType: "control-plane.workspace-deleted.v1",
|
|
211
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
212
|
+
dedupRequired: true
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// ../workflows/lib/detail-types/platform.js
|
|
218
|
+
var require_platform = __commonJS({
|
|
219
|
+
"../workflows/lib/detail-types/platform.js"(exports2) {
|
|
220
|
+
"use strict";
|
|
221
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
222
|
+
exports2.PlatformSystemDataSeededV1 = exports2.PlatformDeploymentCompletedV1 = void 0;
|
|
223
|
+
var sources_1 = require_sources();
|
|
224
|
+
var registry_1 = require_registry();
|
|
225
|
+
exports2.PlatformDeploymentCompletedV1 = (0, registry_1.defineDetailType)({
|
|
226
|
+
detailType: "platform.deployment-completed.v1",
|
|
227
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
228
|
+
dedupRequired: true
|
|
229
|
+
});
|
|
230
|
+
exports2.PlatformSystemDataSeededV1 = (0, registry_1.defineDetailType)({
|
|
231
|
+
detailType: "platform.system-data-seeded.v1",
|
|
232
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
233
|
+
dedupRequired: true
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// ../workflows/lib/detail-types/index.js
|
|
239
|
+
var require_detail_types = __commonJS({
|
|
240
|
+
"../workflows/lib/detail-types/index.js"(exports2) {
|
|
241
|
+
"use strict";
|
|
242
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
243
|
+
if (k2 === void 0) k2 = k;
|
|
244
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
245
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
246
|
+
desc = { enumerable: true, get: function() {
|
|
247
|
+
return m[k];
|
|
248
|
+
} };
|
|
249
|
+
}
|
|
250
|
+
Object.defineProperty(o, k2, desc);
|
|
251
|
+
}) : (function(o, m, k, k2) {
|
|
252
|
+
if (k2 === void 0) k2 = k;
|
|
253
|
+
o[k2] = m[k];
|
|
254
|
+
}));
|
|
255
|
+
var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
|
|
256
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
|
|
257
|
+
};
|
|
258
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
259
|
+
__exportStar(require_control_plane(), exports2);
|
|
260
|
+
__exportStar(require_platform(), exports2);
|
|
261
|
+
__exportStar(require_registry(), exports2);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// ../workflows/lib/publisher.js
|
|
266
|
+
var require_publisher = __commonJS({
|
|
267
|
+
"../workflows/lib/publisher.js"(exports2) {
|
|
268
|
+
"use strict";
|
|
269
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
270
|
+
exports2.WorkflowPublishError = void 0;
|
|
271
|
+
exports2.workflowsClient = workflowsClient;
|
|
272
|
+
exports2.publishWorkflowEvent = publishWorkflowEvent2;
|
|
273
|
+
var node_crypto_1 = require("crypto");
|
|
274
|
+
var client_eventbridge_1 = require("@aws-sdk/client-eventbridge");
|
|
275
|
+
var envelope_version_1 = require_envelope_version();
|
|
276
|
+
var sources_1 = require_sources();
|
|
277
|
+
function workflowsClient(bridge, options = {}) {
|
|
278
|
+
return {
|
|
279
|
+
publish: (entry, payload, ctx) => publishWorkflowEvent2(bridge, entry, payload, ctx, options)
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async function publishWorkflowEvent2(bridge, entry, payload, ctx, options = {}) {
|
|
283
|
+
const eventIdGenerator = options.eventIdGenerator ?? (() => (0, node_crypto_1.randomUUID)());
|
|
284
|
+
const correlationIdGenerator = options.correlationIdGenerator ?? (() => (0, node_crypto_1.randomUUID)());
|
|
285
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
286
|
+
const envelope = {
|
|
287
|
+
eventId: eventIdGenerator(),
|
|
288
|
+
attempt: 1,
|
|
289
|
+
correlationId: ctx.correlationId ?? correlationIdGenerator(),
|
|
290
|
+
causationId: ctx.causationId ?? null,
|
|
291
|
+
actor: ctx.actor,
|
|
292
|
+
occurredAt: now().toISOString(),
|
|
293
|
+
envelopeVersion: envelope_version_1.ENVELOPE_VERSION,
|
|
294
|
+
payload
|
|
295
|
+
};
|
|
296
|
+
const busName = options.busNameByPlane?.[entry.source] ?? sources_1.DEFAULT_BUS_NAME_BY_SOURCE[entry.source];
|
|
297
|
+
const result = await bridge.send(new client_eventbridge_1.PutEventsCommand({
|
|
298
|
+
Entries: [
|
|
299
|
+
{
|
|
300
|
+
EventBusName: busName,
|
|
301
|
+
Source: entry.source,
|
|
302
|
+
DetailType: entry.detailType,
|
|
303
|
+
Detail: JSON.stringify(envelope)
|
|
304
|
+
}
|
|
305
|
+
]
|
|
306
|
+
}));
|
|
307
|
+
if ((result.FailedEntryCount ?? 0) > 0) {
|
|
308
|
+
const first = result.Entries?.[0];
|
|
309
|
+
throw new WorkflowPublishError(`EventBridge rejected ${entry.detailType} publish on bus ${busName}: ${first?.ErrorCode ?? "unknown"} \u2014 ${first?.ErrorMessage ?? "no error message"}`);
|
|
310
|
+
}
|
|
311
|
+
return { eventId: envelope.eventId };
|
|
312
|
+
}
|
|
313
|
+
var WorkflowPublishError = class extends Error {
|
|
314
|
+
/** @param message - human-readable description of the failed publish. */
|
|
315
|
+
constructor(message) {
|
|
316
|
+
super(message);
|
|
317
|
+
this.name = "WorkflowPublishError";
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
exports2.WorkflowPublishError = WorkflowPublishError;
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// ../workflows/lib/consumer.js
|
|
325
|
+
var require_consumer = __commonJS({
|
|
326
|
+
"../workflows/lib/consumer.js"(exports2) {
|
|
327
|
+
"use strict";
|
|
328
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
329
|
+
exports2.UnsupportedEnvelopeVersionError = exports2.InvalidWorkflowEventError = void 0;
|
|
330
|
+
exports2.parseWorkflowEvent = parseWorkflowEvent;
|
|
331
|
+
var envelope_version_1 = require_envelope_version();
|
|
332
|
+
function parseWorkflowEvent(event, expected) {
|
|
333
|
+
if (event.source !== expected.source) {
|
|
334
|
+
throw new InvalidWorkflowEventError(`EventBridge source "${event.source}" does not match expected detail-type's source "${expected.source}".`);
|
|
335
|
+
}
|
|
336
|
+
if (event["detail-type"] !== expected.detailType) {
|
|
337
|
+
throw new InvalidWorkflowEventError(`EventBridge detail-type "${event["detail-type"]}" does not match expected "${expected.detailType}".`);
|
|
338
|
+
}
|
|
339
|
+
const candidate = asEnvelopeCandidate(event.detail);
|
|
340
|
+
if (!(0, envelope_version_1.isSupportedEnvelopeVersion)(candidate.envelopeVersion)) {
|
|
341
|
+
throw new UnsupportedEnvelopeVersionError(`Envelope version "${candidate.envelopeVersion}" is outside the SDK's supported range.`);
|
|
342
|
+
}
|
|
343
|
+
const envelope = {
|
|
344
|
+
eventId: candidate.eventId,
|
|
345
|
+
attempt: candidate.attempt,
|
|
346
|
+
correlationId: candidate.correlationId,
|
|
347
|
+
causationId: candidate.causationId,
|
|
348
|
+
actor: candidate.actor,
|
|
349
|
+
occurredAt: candidate.occurredAt,
|
|
350
|
+
envelopeVersion: candidate.envelopeVersion,
|
|
351
|
+
payload: candidate.payload
|
|
352
|
+
};
|
|
353
|
+
return {
|
|
354
|
+
envelope,
|
|
355
|
+
dedupKey: { eventId: envelope.eventId, attempt: envelope.attempt }
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function asEnvelopeCandidate(detail) {
|
|
359
|
+
if (detail === null || typeof detail !== "object") {
|
|
360
|
+
throw new InvalidWorkflowEventError("EventBridge detail is not a non-null object.");
|
|
361
|
+
}
|
|
362
|
+
const obj = detail;
|
|
363
|
+
assertString(obj, "eventId");
|
|
364
|
+
assertPositiveInteger(obj, "attempt");
|
|
365
|
+
assertString(obj, "correlationId");
|
|
366
|
+
assertCausationId(obj);
|
|
367
|
+
assertActor(obj);
|
|
368
|
+
assertString(obj, "occurredAt");
|
|
369
|
+
assertString(obj, "envelopeVersion");
|
|
370
|
+
if (!("payload" in obj)) {
|
|
371
|
+
throw new InvalidWorkflowEventError("Envelope is missing required field: payload.");
|
|
372
|
+
}
|
|
373
|
+
return obj;
|
|
374
|
+
}
|
|
375
|
+
function assertString(obj, field) {
|
|
376
|
+
const value = obj[field];
|
|
377
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
378
|
+
throw new InvalidWorkflowEventError(`Envelope field "${field}" must be a non-empty string.`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function assertPositiveInteger(obj, field) {
|
|
382
|
+
const value = obj[field];
|
|
383
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
384
|
+
throw new InvalidWorkflowEventError(`Envelope field "${field}" must be a 1-indexed integer.`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function assertCausationId(obj) {
|
|
388
|
+
if (!("causationId" in obj)) {
|
|
389
|
+
throw new InvalidWorkflowEventError("Envelope is missing required field: causationId.");
|
|
390
|
+
}
|
|
391
|
+
const value = obj.causationId;
|
|
392
|
+
if (value !== null && (typeof value !== "string" || value.length === 0)) {
|
|
393
|
+
throw new InvalidWorkflowEventError('Envelope field "causationId" must be a non-empty string or null.');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function assertActor(obj) {
|
|
397
|
+
const actor = obj.actor;
|
|
398
|
+
if (actor === null || typeof actor !== "object") {
|
|
399
|
+
throw new InvalidWorkflowEventError('Envelope field "actor" must be an object.');
|
|
400
|
+
}
|
|
401
|
+
const actorObj = actor;
|
|
402
|
+
const isUserActor = typeof actorObj.ohi_uid === "string" && typeof actorObj.ohi_uname === "string" && typeof actorObj.ohi_tid === "string" && typeof actorObj.ohi_wid === "string";
|
|
403
|
+
const isSystemActor = typeof actorObj.system === "string";
|
|
404
|
+
if (!isUserActor && !isSystemActor) {
|
|
405
|
+
throw new InvalidWorkflowEventError('Envelope field "actor" must be either a user-actor (ohi_tid, ohi_wid, ohi_uid, ohi_uname) or a system-actor ({ system: string }).');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
var InvalidWorkflowEventError = class extends Error {
|
|
409
|
+
/** @param message - human-readable description of the validation failure. */
|
|
410
|
+
constructor(message) {
|
|
411
|
+
super(message);
|
|
412
|
+
this.name = "InvalidWorkflowEventError";
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
exports2.InvalidWorkflowEventError = InvalidWorkflowEventError;
|
|
416
|
+
var UnsupportedEnvelopeVersionError = class extends Error {
|
|
417
|
+
/** @param message - human-readable description of the unsupported version. */
|
|
418
|
+
constructor(message) {
|
|
419
|
+
super(message);
|
|
420
|
+
this.name = "UnsupportedEnvelopeVersionError";
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
exports2.UnsupportedEnvelopeVersionError = UnsupportedEnvelopeVersionError;
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// ../workflows/lib/dedup/env.js
|
|
428
|
+
var require_env = __commonJS({
|
|
429
|
+
"../workflows/lib/dedup/env.js"(exports2) {
|
|
430
|
+
"use strict";
|
|
431
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
432
|
+
exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = void 0;
|
|
433
|
+
exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = "OPENHI_WORKFLOW_DEDUP_TABLE_NAME";
|
|
434
|
+
exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = 14 * 24 * 60 * 60;
|
|
435
|
+
exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = 64;
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// ../workflows/lib/dedup/workflow-dedup-client.js
|
|
440
|
+
var require_workflow_dedup_client = __commonJS({
|
|
441
|
+
"../workflows/lib/dedup/workflow-dedup-client.js"(exports2) {
|
|
442
|
+
"use strict";
|
|
443
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
444
|
+
exports2.WorkflowDedupInvalidInputError = exports2.WorkflowDedupTableNameMissingError = void 0;
|
|
445
|
+
exports2.workflowDedupClient = workflowDedupClient;
|
|
446
|
+
exports2.recordIfAbsent = recordIfAbsent;
|
|
447
|
+
exports2.markFailed = markFailed;
|
|
448
|
+
exports2.encodeSortKey = encodeSortKey;
|
|
449
|
+
var client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
450
|
+
var env_1 = require_env();
|
|
451
|
+
function workflowDedupClient(dynamodb, options = {}) {
|
|
452
|
+
return {
|
|
453
|
+
recordIfAbsent: (input) => recordIfAbsent(dynamodb, input, options),
|
|
454
|
+
markFailed: (input) => markFailed(dynamodb, input, options)
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
async function recordIfAbsent(dynamodb, input, options = {}) {
|
|
458
|
+
assertConsumerName(input.consumerName);
|
|
459
|
+
assertPositiveInteger(input.attempt, "attempt");
|
|
460
|
+
const ttlSeconds = input.ttlSeconds ?? options.defaultTtlSeconds ?? env_1.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;
|
|
461
|
+
if (!Number.isInteger(ttlSeconds) || ttlSeconds <= 0) {
|
|
462
|
+
throw new WorkflowDedupInvalidInputError(`ttlSeconds must be a positive integer; got ${ttlSeconds}.`);
|
|
463
|
+
}
|
|
464
|
+
const tableName = resolveTableName(options.tableName);
|
|
465
|
+
const now = (options.now ?? defaultNow)();
|
|
466
|
+
const sk = encodeSortKey(input.eventId, input.attempt);
|
|
467
|
+
const expiresAt = Math.floor(now.getTime() / 1e3) + ttlSeconds;
|
|
468
|
+
try {
|
|
469
|
+
await dynamodb.send(new client_dynamodb_1.PutItemCommand({
|
|
470
|
+
TableName: tableName,
|
|
471
|
+
Item: {
|
|
472
|
+
consumerName: { S: input.consumerName },
|
|
473
|
+
sk: { S: sk },
|
|
474
|
+
eventId: { S: input.eventId },
|
|
475
|
+
attempt: { N: String(input.attempt) },
|
|
476
|
+
recordedAt: { S: now.toISOString() },
|
|
477
|
+
expiresAt: { N: String(expiresAt) }
|
|
478
|
+
},
|
|
479
|
+
ConditionExpression: "attribute_not_exists(consumerName) AND attribute_not_exists(sk)"
|
|
480
|
+
}));
|
|
481
|
+
return { recorded: true };
|
|
482
|
+
} catch (err) {
|
|
483
|
+
if (err instanceof client_dynamodb_1.ConditionalCheckFailedException) {
|
|
484
|
+
return { recorded: false, alreadyProcessed: true };
|
|
485
|
+
}
|
|
486
|
+
throw err;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
async function markFailed(dynamodb, input, options = {}) {
|
|
490
|
+
assertConsumerName(input.consumerName);
|
|
491
|
+
assertPositiveInteger(input.attempt, "attempt");
|
|
492
|
+
if (input.reason.length === 0) {
|
|
493
|
+
throw new WorkflowDedupInvalidInputError("reason must be non-empty.");
|
|
494
|
+
}
|
|
495
|
+
const tableName = resolveTableName(options.tableName);
|
|
496
|
+
const now = (options.now ?? defaultNow)();
|
|
497
|
+
const sk = encodeSortKey(input.eventId, input.attempt);
|
|
498
|
+
await dynamodb.send(new client_dynamodb_1.UpdateItemCommand({
|
|
499
|
+
TableName: tableName,
|
|
500
|
+
Key: {
|
|
501
|
+
consumerName: { S: input.consumerName },
|
|
502
|
+
sk: { S: sk }
|
|
503
|
+
},
|
|
504
|
+
UpdateExpression: "SET #failed = :failed, #failureReason = :reason, #failedAt = :failedAt",
|
|
505
|
+
ExpressionAttributeNames: {
|
|
506
|
+
"#failed": "failed",
|
|
507
|
+
"#failureReason": "failureReason",
|
|
508
|
+
"#failedAt": "failedAt"
|
|
509
|
+
},
|
|
510
|
+
ExpressionAttributeValues: {
|
|
511
|
+
":failed": { BOOL: true },
|
|
512
|
+
":reason": { S: input.reason },
|
|
513
|
+
":failedAt": { S: now.toISOString() }
|
|
514
|
+
}
|
|
515
|
+
}));
|
|
516
|
+
}
|
|
517
|
+
function encodeSortKey(eventId, attempt) {
|
|
518
|
+
if (eventId.length === 0) {
|
|
519
|
+
throw new WorkflowDedupInvalidInputError("eventId must be non-empty.");
|
|
520
|
+
}
|
|
521
|
+
return `${eventId}#${attempt}`;
|
|
522
|
+
}
|
|
523
|
+
function resolveTableName(explicit) {
|
|
524
|
+
const name = explicit ?? process.env[env_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR];
|
|
525
|
+
if (!name) {
|
|
526
|
+
throw new WorkflowDedupTableNameMissingError(`Workflow dedup table name not set. Pass options.tableName or set ${env_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR}.`);
|
|
527
|
+
}
|
|
528
|
+
return name;
|
|
529
|
+
}
|
|
530
|
+
function assertConsumerName(consumerName) {
|
|
531
|
+
if (consumerName.length === 0) {
|
|
532
|
+
throw new WorkflowDedupInvalidInputError("consumerName must be non-empty.");
|
|
533
|
+
}
|
|
534
|
+
if (consumerName.length > env_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH) {
|
|
535
|
+
throw new WorkflowDedupInvalidInputError(`consumerName must be \u2264${env_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH} chars; got ${consumerName.length}.`);
|
|
536
|
+
}
|
|
537
|
+
if (/\s/.test(consumerName)) {
|
|
538
|
+
throw new WorkflowDedupInvalidInputError("consumerName must not contain whitespace.");
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function assertPositiveInteger(value, field) {
|
|
542
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
543
|
+
throw new WorkflowDedupInvalidInputError(`${field} must be a 1-indexed integer; got ${value}.`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
function defaultNow() {
|
|
547
|
+
return /* @__PURE__ */ new Date();
|
|
548
|
+
}
|
|
549
|
+
var WorkflowDedupTableNameMissingError = class extends Error {
|
|
550
|
+
/** @param message - human-readable description. */
|
|
551
|
+
constructor(message) {
|
|
552
|
+
super(message);
|
|
553
|
+
this.name = "WorkflowDedupTableNameMissingError";
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
exports2.WorkflowDedupTableNameMissingError = WorkflowDedupTableNameMissingError;
|
|
557
|
+
var WorkflowDedupInvalidInputError = class extends Error {
|
|
558
|
+
/** @param message - human-readable description. */
|
|
559
|
+
constructor(message) {
|
|
560
|
+
super(message);
|
|
561
|
+
this.name = "WorkflowDedupInvalidInputError";
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
exports2.WorkflowDedupInvalidInputError = WorkflowDedupInvalidInputError;
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// ../workflows/lib/dedup/index.js
|
|
569
|
+
var require_dedup = __commonJS({
|
|
570
|
+
"../workflows/lib/dedup/index.js"(exports2) {
|
|
571
|
+
"use strict";
|
|
572
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
573
|
+
exports2.workflowDedupClient = exports2.recordIfAbsent = exports2.markFailed = exports2.encodeSortKey = exports2.WorkflowDedupTableNameMissingError = exports2.WorkflowDedupInvalidInputError = exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = void 0;
|
|
574
|
+
var env_1 = require_env();
|
|
575
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS", { enumerable: true, get: function() {
|
|
576
|
+
return env_1.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;
|
|
577
|
+
} });
|
|
578
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH", { enumerable: true, get: function() {
|
|
579
|
+
return env_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH;
|
|
580
|
+
} });
|
|
581
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR", { enumerable: true, get: function() {
|
|
582
|
+
return env_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR;
|
|
583
|
+
} });
|
|
584
|
+
var workflow_dedup_client_1 = require_workflow_dedup_client();
|
|
585
|
+
Object.defineProperty(exports2, "WorkflowDedupInvalidInputError", { enumerable: true, get: function() {
|
|
586
|
+
return workflow_dedup_client_1.WorkflowDedupInvalidInputError;
|
|
587
|
+
} });
|
|
588
|
+
Object.defineProperty(exports2, "WorkflowDedupTableNameMissingError", { enumerable: true, get: function() {
|
|
589
|
+
return workflow_dedup_client_1.WorkflowDedupTableNameMissingError;
|
|
590
|
+
} });
|
|
591
|
+
Object.defineProperty(exports2, "encodeSortKey", { enumerable: true, get: function() {
|
|
592
|
+
return workflow_dedup_client_1.encodeSortKey;
|
|
593
|
+
} });
|
|
594
|
+
Object.defineProperty(exports2, "markFailed", { enumerable: true, get: function() {
|
|
595
|
+
return workflow_dedup_client_1.markFailed;
|
|
596
|
+
} });
|
|
597
|
+
Object.defineProperty(exports2, "recordIfAbsent", { enumerable: true, get: function() {
|
|
598
|
+
return workflow_dedup_client_1.recordIfAbsent;
|
|
599
|
+
} });
|
|
600
|
+
Object.defineProperty(exports2, "workflowDedupClient", { enumerable: true, get: function() {
|
|
601
|
+
return workflow_dedup_client_1.workflowDedupClient;
|
|
602
|
+
} });
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
// ../workflows/lib/index.js
|
|
607
|
+
var require_lib = __commonJS({
|
|
608
|
+
"../workflows/lib/index.js"(exports2) {
|
|
609
|
+
"use strict";
|
|
610
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
611
|
+
exports2.workflowDedupClient = exports2.recordIfAbsent = exports2.markFailed = exports2.encodeSortKey = exports2.WorkflowDedupTableNameMissingError = exports2.WorkflowDedupInvalidInputError = exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = exports2.parseWorkflowEvent = exports2.UnsupportedEnvelopeVersionError = exports2.InvalidWorkflowEventError = exports2.workflowsClient = exports2.publishWorkflowEvent = exports2.WorkflowPublishError = exports2.isWellFormedDetailType = exports2.defineDetailType = exports2.RENAMABLE_ENTITY_TYPE = exports2.PlatformSystemDataSeededV1 = exports2.PlatformDeploymentCompletedV1 = exports2.OWNING_ENTITY_TYPE = exports2.InvalidDetailTypeRegistrationError = exports2.ControlPlaneWorkspaceDeletedV1 = exports2.ControlPlaneWorkspaceCreatedV1 = exports2.ControlPlaneRoleAssignmentDeletedV1 = exports2.ControlPlaneRoleAssignmentCreatedV1 = exports2.ControlPlaneRenameV1 = exports2.ControlPlaneRenameFailedV1 = exports2.ControlPlaneRenameCompleteV1 = exports2.ControlPlaneOwningDeleteV1 = exports2.ControlPlaneOwningDeleteFailedV1 = exports2.ControlPlaneOwningDeleteCompleteV1 = exports2.ControlPlaneMembershipDeletedV1 = exports2.ControlPlaneMembershipCreatedV1 = exports2.OPENHI_OPS_SOURCE = exports2.OPENHI_DATA_SOURCE = exports2.OPENHI_CONTROL_SOURCE = exports2.DEFAULT_BUS_NAME_BY_SOURCE = exports2.workflowUserActorFromClaims = exports2.isWorkflowUserActor = exports2.isWorkflowSystemActor = exports2.MissingActorContextError = exports2.isSupportedEnvelopeVersion = exports2.ENVELOPE_VERSION = void 0;
|
|
612
|
+
var envelope_version_1 = require_envelope_version();
|
|
613
|
+
Object.defineProperty(exports2, "ENVELOPE_VERSION", { enumerable: true, get: function() {
|
|
614
|
+
return envelope_version_1.ENVELOPE_VERSION;
|
|
615
|
+
} });
|
|
616
|
+
Object.defineProperty(exports2, "isSupportedEnvelopeVersion", { enumerable: true, get: function() {
|
|
617
|
+
return envelope_version_1.isSupportedEnvelopeVersion;
|
|
618
|
+
} });
|
|
619
|
+
var envelope_1 = require_envelope();
|
|
620
|
+
Object.defineProperty(exports2, "MissingActorContextError", { enumerable: true, get: function() {
|
|
621
|
+
return envelope_1.MissingActorContextError;
|
|
622
|
+
} });
|
|
623
|
+
Object.defineProperty(exports2, "isWorkflowSystemActor", { enumerable: true, get: function() {
|
|
624
|
+
return envelope_1.isWorkflowSystemActor;
|
|
625
|
+
} });
|
|
626
|
+
Object.defineProperty(exports2, "isWorkflowUserActor", { enumerable: true, get: function() {
|
|
627
|
+
return envelope_1.isWorkflowUserActor;
|
|
628
|
+
} });
|
|
629
|
+
Object.defineProperty(exports2, "workflowUserActorFromClaims", { enumerable: true, get: function() {
|
|
630
|
+
return envelope_1.workflowUserActorFromClaims;
|
|
631
|
+
} });
|
|
632
|
+
var sources_1 = require_sources();
|
|
633
|
+
Object.defineProperty(exports2, "DEFAULT_BUS_NAME_BY_SOURCE", { enumerable: true, get: function() {
|
|
634
|
+
return sources_1.DEFAULT_BUS_NAME_BY_SOURCE;
|
|
635
|
+
} });
|
|
636
|
+
Object.defineProperty(exports2, "OPENHI_CONTROL_SOURCE", { enumerable: true, get: function() {
|
|
637
|
+
return sources_1.OPENHI_CONTROL_SOURCE;
|
|
638
|
+
} });
|
|
639
|
+
Object.defineProperty(exports2, "OPENHI_DATA_SOURCE", { enumerable: true, get: function() {
|
|
640
|
+
return sources_1.OPENHI_DATA_SOURCE;
|
|
641
|
+
} });
|
|
642
|
+
Object.defineProperty(exports2, "OPENHI_OPS_SOURCE", { enumerable: true, get: function() {
|
|
643
|
+
return sources_1.OPENHI_OPS_SOURCE;
|
|
644
|
+
} });
|
|
645
|
+
var detail_types_1 = require_detail_types();
|
|
646
|
+
Object.defineProperty(exports2, "ControlPlaneMembershipCreatedV1", { enumerable: true, get: function() {
|
|
647
|
+
return detail_types_1.ControlPlaneMembershipCreatedV1;
|
|
648
|
+
} });
|
|
649
|
+
Object.defineProperty(exports2, "ControlPlaneMembershipDeletedV1", { enumerable: true, get: function() {
|
|
650
|
+
return detail_types_1.ControlPlaneMembershipDeletedV1;
|
|
651
|
+
} });
|
|
652
|
+
Object.defineProperty(exports2, "ControlPlaneOwningDeleteCompleteV1", { enumerable: true, get: function() {
|
|
653
|
+
return detail_types_1.ControlPlaneOwningDeleteCompleteV1;
|
|
654
|
+
} });
|
|
655
|
+
Object.defineProperty(exports2, "ControlPlaneOwningDeleteFailedV1", { enumerable: true, get: function() {
|
|
656
|
+
return detail_types_1.ControlPlaneOwningDeleteFailedV1;
|
|
657
|
+
} });
|
|
658
|
+
Object.defineProperty(exports2, "ControlPlaneOwningDeleteV1", { enumerable: true, get: function() {
|
|
659
|
+
return detail_types_1.ControlPlaneOwningDeleteV1;
|
|
660
|
+
} });
|
|
661
|
+
Object.defineProperty(exports2, "ControlPlaneRenameCompleteV1", { enumerable: true, get: function() {
|
|
662
|
+
return detail_types_1.ControlPlaneRenameCompleteV1;
|
|
663
|
+
} });
|
|
664
|
+
Object.defineProperty(exports2, "ControlPlaneRenameFailedV1", { enumerable: true, get: function() {
|
|
665
|
+
return detail_types_1.ControlPlaneRenameFailedV1;
|
|
666
|
+
} });
|
|
667
|
+
Object.defineProperty(exports2, "ControlPlaneRenameV1", { enumerable: true, get: function() {
|
|
668
|
+
return detail_types_1.ControlPlaneRenameV1;
|
|
669
|
+
} });
|
|
670
|
+
Object.defineProperty(exports2, "ControlPlaneRoleAssignmentCreatedV1", { enumerable: true, get: function() {
|
|
671
|
+
return detail_types_1.ControlPlaneRoleAssignmentCreatedV1;
|
|
672
|
+
} });
|
|
673
|
+
Object.defineProperty(exports2, "ControlPlaneRoleAssignmentDeletedV1", { enumerable: true, get: function() {
|
|
674
|
+
return detail_types_1.ControlPlaneRoleAssignmentDeletedV1;
|
|
675
|
+
} });
|
|
676
|
+
Object.defineProperty(exports2, "ControlPlaneWorkspaceCreatedV1", { enumerable: true, get: function() {
|
|
677
|
+
return detail_types_1.ControlPlaneWorkspaceCreatedV1;
|
|
678
|
+
} });
|
|
679
|
+
Object.defineProperty(exports2, "ControlPlaneWorkspaceDeletedV1", { enumerable: true, get: function() {
|
|
680
|
+
return detail_types_1.ControlPlaneWorkspaceDeletedV1;
|
|
681
|
+
} });
|
|
682
|
+
Object.defineProperty(exports2, "InvalidDetailTypeRegistrationError", { enumerable: true, get: function() {
|
|
683
|
+
return detail_types_1.InvalidDetailTypeRegistrationError;
|
|
684
|
+
} });
|
|
685
|
+
Object.defineProperty(exports2, "OWNING_ENTITY_TYPE", { enumerable: true, get: function() {
|
|
686
|
+
return detail_types_1.OWNING_ENTITY_TYPE;
|
|
687
|
+
} });
|
|
688
|
+
Object.defineProperty(exports2, "PlatformDeploymentCompletedV1", { enumerable: true, get: function() {
|
|
689
|
+
return detail_types_1.PlatformDeploymentCompletedV1;
|
|
690
|
+
} });
|
|
691
|
+
Object.defineProperty(exports2, "PlatformSystemDataSeededV1", { enumerable: true, get: function() {
|
|
692
|
+
return detail_types_1.PlatformSystemDataSeededV1;
|
|
693
|
+
} });
|
|
694
|
+
Object.defineProperty(exports2, "RENAMABLE_ENTITY_TYPE", { enumerable: true, get: function() {
|
|
695
|
+
return detail_types_1.RENAMABLE_ENTITY_TYPE;
|
|
696
|
+
} });
|
|
697
|
+
Object.defineProperty(exports2, "defineDetailType", { enumerable: true, get: function() {
|
|
698
|
+
return detail_types_1.defineDetailType;
|
|
699
|
+
} });
|
|
700
|
+
Object.defineProperty(exports2, "isWellFormedDetailType", { enumerable: true, get: function() {
|
|
701
|
+
return detail_types_1.isWellFormedDetailType;
|
|
702
|
+
} });
|
|
703
|
+
var publisher_1 = require_publisher();
|
|
704
|
+
Object.defineProperty(exports2, "WorkflowPublishError", { enumerable: true, get: function() {
|
|
705
|
+
return publisher_1.WorkflowPublishError;
|
|
706
|
+
} });
|
|
707
|
+
Object.defineProperty(exports2, "publishWorkflowEvent", { enumerable: true, get: function() {
|
|
708
|
+
return publisher_1.publishWorkflowEvent;
|
|
709
|
+
} });
|
|
710
|
+
Object.defineProperty(exports2, "workflowsClient", { enumerable: true, get: function() {
|
|
711
|
+
return publisher_1.workflowsClient;
|
|
712
|
+
} });
|
|
713
|
+
var consumer_1 = require_consumer();
|
|
714
|
+
Object.defineProperty(exports2, "InvalidWorkflowEventError", { enumerable: true, get: function() {
|
|
715
|
+
return consumer_1.InvalidWorkflowEventError;
|
|
716
|
+
} });
|
|
717
|
+
Object.defineProperty(exports2, "UnsupportedEnvelopeVersionError", { enumerable: true, get: function() {
|
|
718
|
+
return consumer_1.UnsupportedEnvelopeVersionError;
|
|
719
|
+
} });
|
|
720
|
+
Object.defineProperty(exports2, "parseWorkflowEvent", { enumerable: true, get: function() {
|
|
721
|
+
return consumer_1.parseWorkflowEvent;
|
|
722
|
+
} });
|
|
723
|
+
var dedup_1 = require_dedup();
|
|
724
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS", { enumerable: true, get: function() {
|
|
725
|
+
return dedup_1.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;
|
|
726
|
+
} });
|
|
727
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH", { enumerable: true, get: function() {
|
|
728
|
+
return dedup_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH;
|
|
729
|
+
} });
|
|
730
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR", { enumerable: true, get: function() {
|
|
731
|
+
return dedup_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR;
|
|
732
|
+
} });
|
|
733
|
+
Object.defineProperty(exports2, "WorkflowDedupInvalidInputError", { enumerable: true, get: function() {
|
|
734
|
+
return dedup_1.WorkflowDedupInvalidInputError;
|
|
735
|
+
} });
|
|
736
|
+
Object.defineProperty(exports2, "WorkflowDedupTableNameMissingError", { enumerable: true, get: function() {
|
|
737
|
+
return dedup_1.WorkflowDedupTableNameMissingError;
|
|
738
|
+
} });
|
|
739
|
+
Object.defineProperty(exports2, "encodeSortKey", { enumerable: true, get: function() {
|
|
740
|
+
return dedup_1.encodeSortKey;
|
|
741
|
+
} });
|
|
742
|
+
Object.defineProperty(exports2, "markFailed", { enumerable: true, get: function() {
|
|
743
|
+
return dedup_1.markFailed;
|
|
744
|
+
} });
|
|
745
|
+
Object.defineProperty(exports2, "recordIfAbsent", { enumerable: true, get: function() {
|
|
746
|
+
return dedup_1.recordIfAbsent;
|
|
747
|
+
} });
|
|
748
|
+
Object.defineProperty(exports2, "workflowDedupClient", { enumerable: true, get: function() {
|
|
749
|
+
return dedup_1.workflowDedupClient;
|
|
750
|
+
} });
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
|
|
30
754
|
// src/data/lambda/rest-api-lambda.handler.ts
|
|
31
755
|
var rest_api_lambda_handler_exports = {};
|
|
32
756
|
__export(rest_api_lambda_handler_exports, {
|
|
@@ -1675,6 +2399,24 @@ var TenantEntity = new import_electrodb11.Entity({
|
|
|
1675
2399
|
type: "string",
|
|
1676
2400
|
required: true
|
|
1677
2401
|
},
|
|
2402
|
+
/**
|
|
2403
|
+
* ADR-028 denormalized counter — number of tenant-scoped Memberships
|
|
2404
|
+
* (users) in this tenant. Maintained by the counter-maintenance
|
|
2405
|
+
* consumer via atomic ADD; absent/0 until first event or reconciliation.
|
|
2406
|
+
*/
|
|
2407
|
+
usersInTenant: {
|
|
2408
|
+
type: "number",
|
|
2409
|
+
required: false
|
|
2410
|
+
},
|
|
2411
|
+
/**
|
|
2412
|
+
* ADR-028 denormalized counter — number of Workspaces in this tenant.
|
|
2413
|
+
* Maintained by the counter-maintenance consumer via atomic ADD;
|
|
2414
|
+
* absent/0 until first event or reconciliation.
|
|
2415
|
+
*/
|
|
2416
|
+
workspacesInTenant: {
|
|
2417
|
+
type: "number",
|
|
2418
|
+
required: false
|
|
2419
|
+
},
|
|
1678
2420
|
gsi1Shard: gsi1ShardAttribute,
|
|
1679
2421
|
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
1680
2422
|
gsi1sk: gsi1skAttribute,
|
|
@@ -1779,6 +2521,26 @@ var UserEntity = new import_electrodb12.Entity({
|
|
|
1779
2521
|
type: "string",
|
|
1780
2522
|
required: true
|
|
1781
2523
|
},
|
|
2524
|
+
/**
|
|
2525
|
+
* ADR-028 denormalized counter — number of tenant-scoped Memberships
|
|
2526
|
+
* (tenants) this user belongs to. Maintained by the
|
|
2527
|
+
* counter-maintenance consumer via atomic ADD; absent/0 until first
|
|
2528
|
+
* event or reconciliation.
|
|
2529
|
+
*/
|
|
2530
|
+
tenantsForUser: {
|
|
2531
|
+
type: "number",
|
|
2532
|
+
required: false
|
|
2533
|
+
},
|
|
2534
|
+
/**
|
|
2535
|
+
* ADR-028 denormalized counter — number of workspace-scoped
|
|
2536
|
+
* Memberships (workspaces) this user belongs to. Maintained by the
|
|
2537
|
+
* counter-maintenance consumer via atomic ADD; absent/0 until first
|
|
2538
|
+
* event or reconciliation.
|
|
2539
|
+
*/
|
|
2540
|
+
workspacesForUser: {
|
|
2541
|
+
type: "number",
|
|
2542
|
+
required: false
|
|
2543
|
+
},
|
|
1782
2544
|
gsi1Shard: gsi1ShardAttribute,
|
|
1783
2545
|
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
1784
2546
|
gsi1sk: gsi1skAttribute,
|
|
@@ -1923,6 +2685,36 @@ var WorkspaceEntity = new import_electrodb13.Entity({
|
|
|
1923
2685
|
type: "string",
|
|
1924
2686
|
required: true
|
|
1925
2687
|
},
|
|
2688
|
+
/**
|
|
2689
|
+
* ADR-028 denormalized counter — number of workspace-scoped
|
|
2690
|
+
* Memberships (users) in this workspace. Maintained by the
|
|
2691
|
+
* counter-maintenance consumer via atomic ADD; absent/0 until first
|
|
2692
|
+
* event or reconciliation.
|
|
2693
|
+
*/
|
|
2694
|
+
usersInWorkspace: {
|
|
2695
|
+
type: "number",
|
|
2696
|
+
required: false
|
|
2697
|
+
},
|
|
2698
|
+
/**
|
|
2699
|
+
* ADR-028 denormalized counter — number of workspace-scoped
|
|
2700
|
+
* RoleAssignments classified as admin-tier in this workspace.
|
|
2701
|
+
* Maintained by the counter-maintenance consumer via atomic ADD;
|
|
2702
|
+
* absent/0 until first event or reconciliation.
|
|
2703
|
+
*/
|
|
2704
|
+
adminUsersInWorkspace: {
|
|
2705
|
+
type: "number",
|
|
2706
|
+
required: false
|
|
2707
|
+
},
|
|
2708
|
+
/**
|
|
2709
|
+
* ADR-028 denormalized counter — number of workspace-scoped
|
|
2710
|
+
* RoleAssignments classified as non-admin in this workspace.
|
|
2711
|
+
* Maintained by the counter-maintenance consumer via atomic ADD;
|
|
2712
|
+
* absent/0 until first event or reconciliation.
|
|
2713
|
+
*/
|
|
2714
|
+
normalUsersInWorkspace: {
|
|
2715
|
+
type: "number",
|
|
2716
|
+
required: false
|
|
2717
|
+
},
|
|
1926
2718
|
gsi1Shard: gsi1ShardAttribute,
|
|
1927
2719
|
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
1928
2720
|
gsi1sk: gsi1skAttribute,
|
|
@@ -3240,7 +4032,7 @@ function sendInvalidSummary400(res, diagnostics) {
|
|
|
3240
4032
|
}
|
|
3241
4033
|
async function handleListRoute(opts) {
|
|
3242
4034
|
const { req, res, basePath, listOperation, errorLogContext } = opts;
|
|
3243
|
-
const ctx = req.openhiContext;
|
|
4035
|
+
const ctx = opts.context ?? req.openhiContext;
|
|
3244
4036
|
const parsed = parseSearchSubsetting(req, res);
|
|
3245
4037
|
if ("errorResponse" in parsed) return parsed.errorResponse;
|
|
3246
4038
|
try {
|
|
@@ -4933,6 +5725,76 @@ function buildMembershipWorkspaceProjectionItem(input) {
|
|
|
4933
5725
|
};
|
|
4934
5726
|
}
|
|
4935
5727
|
|
|
5728
|
+
// src/data/operations/control/control-event-publisher.ts
|
|
5729
|
+
var import_client_eventbridge = require("@aws-sdk/client-eventbridge");
|
|
5730
|
+
var import_workflows = __toESM(require_lib());
|
|
5731
|
+
var CONTROL_EVENT_BUS_NAME_ENV_VAR = "CONTROL_EVENT_BUS_NAME";
|
|
5732
|
+
var cachedClient;
|
|
5733
|
+
function getClient() {
|
|
5734
|
+
if (!cachedClient) {
|
|
5735
|
+
cachedClient = new import_client_eventbridge.EventBridgeClient({
|
|
5736
|
+
region: process.env.AWS_REGION ?? "us-east-1"
|
|
5737
|
+
});
|
|
5738
|
+
}
|
|
5739
|
+
return cachedClient;
|
|
5740
|
+
}
|
|
5741
|
+
function actorFromContext(context) {
|
|
5742
|
+
return {
|
|
5743
|
+
ohi_tid: context.tenantId,
|
|
5744
|
+
ohi_wid: context.workspaceId,
|
|
5745
|
+
ohi_uid: context.actorId,
|
|
5746
|
+
ohi_uname: context.actorName
|
|
5747
|
+
};
|
|
5748
|
+
}
|
|
5749
|
+
async function publishControlEvent(entry, payload, context) {
|
|
5750
|
+
const busName = process.env[CONTROL_EVENT_BUS_NAME_ENV_VAR];
|
|
5751
|
+
if (!busName) {
|
|
5752
|
+
return;
|
|
5753
|
+
}
|
|
5754
|
+
try {
|
|
5755
|
+
await (0, import_workflows.publishWorkflowEvent)(
|
|
5756
|
+
getClient(),
|
|
5757
|
+
entry,
|
|
5758
|
+
payload,
|
|
5759
|
+
{ actor: actorFromContext(context) },
|
|
5760
|
+
{ busNameByPlane: { [import_workflows.OPENHI_CONTROL_SOURCE]: busName } }
|
|
5761
|
+
);
|
|
5762
|
+
} catch (err) {
|
|
5763
|
+
console.error(`control-event publish failed for ${entry.detailType}:`, err);
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
async function publishMembershipCreated(context, detail) {
|
|
5767
|
+
await publishControlEvent(import_workflows.ControlPlaneMembershipCreatedV1, detail, context);
|
|
5768
|
+
}
|
|
5769
|
+
async function publishMembershipDeleted(context, detail) {
|
|
5770
|
+
await publishControlEvent(import_workflows.ControlPlaneMembershipDeletedV1, detail, context);
|
|
5771
|
+
}
|
|
5772
|
+
async function publishRoleAssignmentCreated(context, detail) {
|
|
5773
|
+
await publishControlEvent(
|
|
5774
|
+
import_workflows.ControlPlaneRoleAssignmentCreatedV1,
|
|
5775
|
+
detail,
|
|
5776
|
+
context
|
|
5777
|
+
);
|
|
5778
|
+
}
|
|
5779
|
+
async function publishRoleAssignmentDeleted(context, detail) {
|
|
5780
|
+
await publishControlEvent(
|
|
5781
|
+
import_workflows.ControlPlaneRoleAssignmentDeletedV1,
|
|
5782
|
+
detail,
|
|
5783
|
+
context
|
|
5784
|
+
);
|
|
5785
|
+
}
|
|
5786
|
+
async function publishWorkspaceCreated(context, detail) {
|
|
5787
|
+
await publishControlEvent(import_workflows.ControlPlaneWorkspaceCreatedV1, detail, context);
|
|
5788
|
+
}
|
|
5789
|
+
async function publishWorkspaceDeleted(context, detail) {
|
|
5790
|
+
await publishControlEvent(import_workflows.ControlPlaneWorkspaceDeletedV1, detail, context);
|
|
5791
|
+
}
|
|
5792
|
+
function extractRoleLevel(resource) {
|
|
5793
|
+
const code = resource?.code;
|
|
5794
|
+
const first = code?.coding?.[0]?.code;
|
|
5795
|
+
return typeof first === "string" && first.length > 0 ? first : void 0;
|
|
5796
|
+
}
|
|
5797
|
+
|
|
4936
5798
|
// src/data/operations/control/denormalized-display-names.ts
|
|
4937
5799
|
function extractDenormalizedReferenceDisplay(resource, fieldName) {
|
|
4938
5800
|
const field = resource[fieldName];
|
|
@@ -5060,6 +5922,14 @@ async function createMembershipOperation(params) {
|
|
|
5060
5922
|
});
|
|
5061
5923
|
}
|
|
5062
5924
|
await executeMultiWrite({ service, triples });
|
|
5925
|
+
await publishMembershipCreated(context, {
|
|
5926
|
+
membershipId: id,
|
|
5927
|
+
tenantId: context.tenantId,
|
|
5928
|
+
...userIdFromResource !== void 0 && { userId: userIdFromResource },
|
|
5929
|
+
...workspaceIdFromResource !== void 0 && {
|
|
5930
|
+
workspaceId: workspaceIdFromResource
|
|
5931
|
+
}
|
|
5932
|
+
});
|
|
5063
5933
|
return {
|
|
5064
5934
|
id,
|
|
5065
5935
|
resource,
|
|
@@ -5186,6 +6056,14 @@ async function deleteMembershipOperation(params) {
|
|
|
5186
6056
|
});
|
|
5187
6057
|
}
|
|
5188
6058
|
await executeMultiWrite({ service, triples });
|
|
6059
|
+
await publishMembershipDeleted(context, {
|
|
6060
|
+
membershipId: id,
|
|
6061
|
+
tenantId: context.tenantId,
|
|
6062
|
+
...userIdFromResource !== void 0 && { userId: userIdFromResource },
|
|
6063
|
+
...workspaceIdFromResource !== void 0 && {
|
|
6064
|
+
workspaceId: workspaceIdFromResource
|
|
6065
|
+
}
|
|
6066
|
+
});
|
|
5189
6067
|
}
|
|
5190
6068
|
|
|
5191
6069
|
// src/data/rest-api/routes/control/membership/membership-delete-route.ts
|
|
@@ -5270,69 +6148,209 @@ async function listMembershipsOperation(params) {
|
|
|
5270
6148
|
});
|
|
5271
6149
|
}
|
|
5272
6150
|
|
|
5273
|
-
// src/data/rest-api/routes/control/
|
|
5274
|
-
|
|
5275
|
-
return
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
basePath: BASE_PATH.MEMBERSHIP,
|
|
5279
|
-
listOperation: listMembershipsOperation,
|
|
5280
|
-
errorLogContext: "GET /Membership list error:"
|
|
6151
|
+
// src/data/rest-api/routes/control/cross-cutting-route-helpers.ts
|
|
6152
|
+
function sendInvalidQuery400(res, diagnostics) {
|
|
6153
|
+
return res.status(400).json({
|
|
6154
|
+
resourceType: "OperationOutcome",
|
|
6155
|
+
issue: [{ severity: "error", code: "invalid", diagnostics }]
|
|
5281
6156
|
});
|
|
5282
6157
|
}
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
const { context, id, body, tableName } = params;
|
|
5288
|
-
const service = getDynamoControlService(tableName);
|
|
5289
|
-
const existing = await service.entities.membership.get({ tenantId: context.tenantId, id, sk: "CURRENT" }).go();
|
|
5290
|
-
if (!existing.data) {
|
|
5291
|
-
throw new NotFoundError(`Membership not found: ${id}`);
|
|
6158
|
+
function resolveTenantScopeOverride(req, res, context) {
|
|
6159
|
+
const raw = (req.query ?? {}).tenant;
|
|
6160
|
+
if (raw === void 0) {
|
|
6161
|
+
return { ok: true, context };
|
|
5292
6162
|
}
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
);
|
|
5302
|
-
linkedDataIdentityRef = ext?.valueReference?.reference;
|
|
5303
|
-
} catch (e) {
|
|
5304
|
-
if (e instanceof import_types9.LinkedDataIdentityCardinalityError) {
|
|
5305
|
-
throw new ValidationError(e.message, { cause: e });
|
|
5306
|
-
}
|
|
5307
|
-
throw e;
|
|
6163
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
6164
|
+
return {
|
|
6165
|
+
ok: false,
|
|
6166
|
+
response: sendInvalidQuery400(
|
|
6167
|
+
res,
|
|
6168
|
+
"Query parameter `tenant` must be a non-empty string."
|
|
6169
|
+
)
|
|
6170
|
+
};
|
|
5308
6171
|
}
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
const
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
6172
|
+
return { ok: true, context: { ...context, tenantId: raw } };
|
|
6173
|
+
}
|
|
6174
|
+
function sendForbidden403(res, diagnostics) {
|
|
6175
|
+
return res.status(403).json({
|
|
6176
|
+
resourceType: "OperationOutcome",
|
|
6177
|
+
issue: [{ severity: "error", code: "forbidden", diagnostics }]
|
|
6178
|
+
});
|
|
6179
|
+
}
|
|
6180
|
+
var COMMON_KEYS = ["cursor", "limit", "order"];
|
|
6181
|
+
function parseCommonListQuery(req, res, options = {}) {
|
|
6182
|
+
const q = req.query ?? {};
|
|
6183
|
+
const allowedKeys = /* @__PURE__ */ new Set([
|
|
6184
|
+
...COMMON_KEYS,
|
|
6185
|
+
...options.extraKeys ?? []
|
|
6186
|
+
]);
|
|
6187
|
+
const extra = Object.keys(q).filter((k) => !allowedKeys.has(k));
|
|
6188
|
+
if (extra.length > 0) {
|
|
6189
|
+
return {
|
|
6190
|
+
ok: false,
|
|
6191
|
+
response: sendInvalidQuery400(
|
|
6192
|
+
res,
|
|
6193
|
+
`Unsupported query parameter${extra.length === 1 ? "" : "s"}: ${extra.join(", ")}.`
|
|
6194
|
+
)
|
|
6195
|
+
};
|
|
6196
|
+
}
|
|
6197
|
+
const rawCursor = q.cursor;
|
|
6198
|
+
let cursor = null;
|
|
6199
|
+
if (rawCursor !== void 0) {
|
|
6200
|
+
if (typeof rawCursor !== "string") {
|
|
6201
|
+
return {
|
|
6202
|
+
ok: false,
|
|
6203
|
+
response: sendInvalidQuery400(
|
|
6204
|
+
res,
|
|
6205
|
+
"Query parameter `cursor` must be a string."
|
|
6206
|
+
)
|
|
6207
|
+
};
|
|
6208
|
+
}
|
|
6209
|
+
cursor = rawCursor;
|
|
6210
|
+
}
|
|
6211
|
+
const rawLimit = q.limit;
|
|
6212
|
+
let limit;
|
|
6213
|
+
if (rawLimit !== void 0) {
|
|
6214
|
+
if (typeof rawLimit !== "string" || rawLimit.length === 0) {
|
|
6215
|
+
return {
|
|
6216
|
+
ok: false,
|
|
6217
|
+
response: sendInvalidQuery400(
|
|
6218
|
+
res,
|
|
6219
|
+
"Query parameter `limit` must be a positive integer."
|
|
6220
|
+
)
|
|
6221
|
+
};
|
|
6222
|
+
}
|
|
6223
|
+
const parsed = Number(rawLimit);
|
|
6224
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
6225
|
+
return {
|
|
6226
|
+
ok: false,
|
|
6227
|
+
response: sendInvalidQuery400(
|
|
6228
|
+
res,
|
|
6229
|
+
"Query parameter `limit` must be a positive integer."
|
|
6230
|
+
)
|
|
6231
|
+
};
|
|
6232
|
+
}
|
|
6233
|
+
limit = parsed;
|
|
6234
|
+
}
|
|
6235
|
+
const rawOrder = q.order;
|
|
6236
|
+
let order;
|
|
6237
|
+
if (rawOrder !== void 0) {
|
|
6238
|
+
if (rawOrder !== "asc" && rawOrder !== "desc") {
|
|
6239
|
+
return {
|
|
6240
|
+
ok: false,
|
|
6241
|
+
response: sendInvalidQuery400(
|
|
6242
|
+
res,
|
|
6243
|
+
'Query parameter `order` must be one of "asc", "desc".'
|
|
6244
|
+
)
|
|
6245
|
+
};
|
|
6246
|
+
}
|
|
6247
|
+
order = rawOrder;
|
|
6248
|
+
}
|
|
6249
|
+
const value = order === void 0 ? limit === void 0 ? { cursor } : { cursor, limit } : limit === void 0 ? { cursor, order } : { cursor, limit, order };
|
|
6250
|
+
return { ok: true, value };
|
|
6251
|
+
}
|
|
6252
|
+
function buildPaginationLinks(opts) {
|
|
6253
|
+
const links = [
|
|
6254
|
+
{ relation: "self", url: composeUrl(opts.basePath, opts.query) }
|
|
6255
|
+
];
|
|
6256
|
+
if (opts.nextCursor !== null && opts.nextCursor.length > 0) {
|
|
6257
|
+
const nextQuery = { ...opts.query };
|
|
6258
|
+
nextQuery.cursor = opts.nextCursor;
|
|
6259
|
+
links.push({
|
|
6260
|
+
relation: "next",
|
|
6261
|
+
url: composeUrl(opts.basePath, nextQuery)
|
|
6262
|
+
});
|
|
6263
|
+
}
|
|
6264
|
+
return links;
|
|
6265
|
+
}
|
|
6266
|
+
function composeUrl(basePath, query) {
|
|
6267
|
+
const usp = new URLSearchParams();
|
|
6268
|
+
for (const [key, value] of Object.entries(query)) {
|
|
6269
|
+
if (value === void 0 || value === null) {
|
|
6270
|
+
continue;
|
|
6271
|
+
}
|
|
6272
|
+
if (Array.isArray(value)) {
|
|
6273
|
+
for (const v of value) {
|
|
6274
|
+
if (v !== void 0 && v !== null) {
|
|
6275
|
+
usp.append(key, String(v));
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
} else {
|
|
6279
|
+
usp.append(key, String(value));
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
const qs = usp.toString();
|
|
6283
|
+
return qs.length === 0 ? basePath : `${basePath}?${qs}`;
|
|
6284
|
+
}
|
|
6285
|
+
|
|
6286
|
+
// src/data/rest-api/routes/control/membership/membership-list-route.ts
|
|
6287
|
+
async function listMembershipsRoute(req, res) {
|
|
6288
|
+
const scope = resolveTenantScopeOverride(req, res, req.openhiContext);
|
|
6289
|
+
if (!scope.ok) {
|
|
6290
|
+
return scope.response;
|
|
6291
|
+
}
|
|
6292
|
+
return handleListRoute({
|
|
6293
|
+
req,
|
|
6294
|
+
res,
|
|
6295
|
+
basePath: BASE_PATH.MEMBERSHIP,
|
|
6296
|
+
listOperation: listMembershipsOperation,
|
|
6297
|
+
errorLogContext: "GET /Membership list error:",
|
|
6298
|
+
context: scope.context
|
|
6299
|
+
});
|
|
6300
|
+
}
|
|
6301
|
+
|
|
6302
|
+
// src/data/operations/control/membership/membership-update-operation.ts
|
|
6303
|
+
var import_types9 = require("@openhi/types");
|
|
6304
|
+
async function updateMembershipOperation(params) {
|
|
6305
|
+
const { context, id, body, tableName } = params;
|
|
6306
|
+
const service = getDynamoControlService(tableName);
|
|
6307
|
+
const existing = await service.entities.membership.get({ tenantId: context.tenantId, id, sk: "CURRENT" }).go();
|
|
6308
|
+
if (!existing.data) {
|
|
6309
|
+
throw new NotFoundError(`Membership not found: ${id}`);
|
|
6310
|
+
}
|
|
6311
|
+
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
6312
|
+
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
6313
|
+
const vid = `${Date.now()}`;
|
|
6314
|
+
const resource = { resourceType: "Membership", id, ...parsedResource };
|
|
6315
|
+
let linkedDataIdentityRef;
|
|
6316
|
+
try {
|
|
6317
|
+
const ext = (0, import_types9.assertLinkedDataIdentityCardinality)(
|
|
6318
|
+
resource
|
|
6319
|
+
);
|
|
6320
|
+
linkedDataIdentityRef = ext?.valueReference?.reference;
|
|
6321
|
+
} catch (e) {
|
|
6322
|
+
if (e instanceof import_types9.LinkedDataIdentityCardinalityError) {
|
|
6323
|
+
throw new ValidationError(e.message, { cause: e });
|
|
6324
|
+
}
|
|
6325
|
+
throw e;
|
|
6326
|
+
}
|
|
6327
|
+
const resourceRecord = resource;
|
|
6328
|
+
const denormalizedTenantName = extractDenormalizedReferenceDisplay(
|
|
6329
|
+
resourceRecord,
|
|
6330
|
+
"tenant"
|
|
6331
|
+
);
|
|
6332
|
+
const denormalizedUserName = extractDenormalizedReferenceDisplay(
|
|
6333
|
+
resourceRecord,
|
|
6334
|
+
"user"
|
|
6335
|
+
);
|
|
6336
|
+
const denormalizedWorkspaceName = extractDenormalizedReferenceDisplay(
|
|
6337
|
+
resourceRecord,
|
|
6338
|
+
"workspace"
|
|
6339
|
+
);
|
|
6340
|
+
const summary = JSON.stringify((0, import_types9.extractSummary)(resource));
|
|
6341
|
+
const userIdFromResource = extractReferenceSlug(resourceRecord, "user");
|
|
6342
|
+
const workspaceIdFromResource = extractReferenceSlug(
|
|
6343
|
+
resourceRecord,
|
|
6344
|
+
"workspace"
|
|
6345
|
+
);
|
|
6346
|
+
const userProjectionItem = userIdFromResource !== void 0 ? buildMembershipUserProjectionItem({
|
|
6347
|
+
tenantId: context.tenantId,
|
|
6348
|
+
userId: userIdFromResource,
|
|
6349
|
+
workspaceId: workspaceIdFromResource,
|
|
6350
|
+
membershipId: id,
|
|
6351
|
+
summary,
|
|
6352
|
+
vid,
|
|
6353
|
+
lastUpdated,
|
|
5336
6354
|
denormalizedTenantName,
|
|
5337
6355
|
denormalizedUserName,
|
|
5338
6356
|
denormalizedWorkspaceName
|
|
@@ -5902,6 +6920,18 @@ async function createRoleAssignmentOperation(params) {
|
|
|
5902
6920
|
});
|
|
5903
6921
|
}
|
|
5904
6922
|
await executeMultiWrite({ service, triples });
|
|
6923
|
+
await publishRoleAssignmentCreated(context, {
|
|
6924
|
+
roleAssignmentId: id,
|
|
6925
|
+
tenantId: context.tenantId,
|
|
6926
|
+
...userIdFromResource !== void 0 && { userId: userIdFromResource },
|
|
6927
|
+
...workspaceIdFromResource !== void 0 && {
|
|
6928
|
+
workspaceId: workspaceIdFromResource
|
|
6929
|
+
},
|
|
6930
|
+
...roleIdFromResource !== void 0 && { roleId: roleIdFromResource },
|
|
6931
|
+
...extractRoleLevel(resourceRecord) !== void 0 && {
|
|
6932
|
+
roleLevel: extractRoleLevel(resourceRecord)
|
|
6933
|
+
}
|
|
6934
|
+
});
|
|
5905
6935
|
return {
|
|
5906
6936
|
id,
|
|
5907
6937
|
resource,
|
|
@@ -6029,6 +7059,18 @@ async function deleteRoleAssignmentOperation(params) {
|
|
|
6029
7059
|
});
|
|
6030
7060
|
}
|
|
6031
7061
|
await executeMultiWrite({ service, triples });
|
|
7062
|
+
await publishRoleAssignmentDeleted(context, {
|
|
7063
|
+
roleAssignmentId: id,
|
|
7064
|
+
tenantId: context.tenantId,
|
|
7065
|
+
...userIdFromResource !== void 0 && { userId: userIdFromResource },
|
|
7066
|
+
...workspaceIdFromResource !== void 0 && {
|
|
7067
|
+
workspaceId: workspaceIdFromResource
|
|
7068
|
+
},
|
|
7069
|
+
...roleIdFromResource !== void 0 && { roleId: roleIdFromResource },
|
|
7070
|
+
...extractRoleLevel(parsed) !== void 0 && {
|
|
7071
|
+
roleLevel: extractRoleLevel(parsed)
|
|
7072
|
+
}
|
|
7073
|
+
});
|
|
6032
7074
|
}
|
|
6033
7075
|
|
|
6034
7076
|
// src/data/rest-api/routes/control/roleassignment/roleassignment-delete-route.ts
|
|
@@ -6440,6 +7482,9 @@ async function getTenantByIdRoute(req, res) {
|
|
|
6440
7482
|
|
|
6441
7483
|
// src/data/operations/control/tenant/tenant-list-operation.ts
|
|
6442
7484
|
var SK9 = "CURRENT";
|
|
7485
|
+
function counterValue(value) {
|
|
7486
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
7487
|
+
}
|
|
6443
7488
|
async function listTenantsOperation(params) {
|
|
6444
7489
|
const { tableName, mode = "full" } = params;
|
|
6445
7490
|
const service = getDynamoControlService(tableName);
|
|
@@ -6449,40 +7494,278 @@ async function listTenantsOperation(params) {
|
|
|
6449
7494
|
(_, shard) => service.entities.tenant.query.gsi1({ gsi1Shard: String(shard) }).go()
|
|
6450
7495
|
)
|
|
6451
7496
|
);
|
|
6452
|
-
return dispatchListMode(
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
7497
|
+
return dispatchListMode(mode, shardResults, {
|
|
7498
|
+
hydrate: (orderedIds) => batchGetWithRetry(
|
|
7499
|
+
service.entities.tenant,
|
|
7500
|
+
orderedIds.map((id) => ({ tenantId: id, sk: SK9 }))
|
|
7501
|
+
),
|
|
7502
|
+
getId: (item) => item.id,
|
|
7503
|
+
// FULL mode (admin list default): read the ADR-028 counters off the
|
|
7504
|
+
// canonical record hydrated by BatchGet and expose them as
|
|
7505
|
+
// `resource.counts`. Missing counters render as 0.
|
|
7506
|
+
buildEntry: (id, item) => ({
|
|
7507
|
+
id,
|
|
7508
|
+
resource: {
|
|
7509
|
+
resourceType: "Tenant",
|
|
6462
7510
|
id,
|
|
6463
|
-
resource
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
7511
|
+
...JSON.parse(item.resource),
|
|
7512
|
+
counts: {
|
|
7513
|
+
usersInTenant: counterValue(item.usersInTenant),
|
|
7514
|
+
workspacesInTenant: counterValue(item.workspacesInTenant)
|
|
6467
7515
|
}
|
|
6468
|
-
}
|
|
6469
|
-
|
|
7516
|
+
}
|
|
7517
|
+
}),
|
|
7518
|
+
// SUMMARY mode reads only the GSI1 `summary` projection, which does
|
|
7519
|
+
// not carry the counters; surface zeros so the shape stays uniform.
|
|
7520
|
+
buildSummaryEntry: (id, parsed) => ({
|
|
7521
|
+
id,
|
|
7522
|
+
resource: {
|
|
7523
|
+
resourceType: "Tenant",
|
|
6470
7524
|
id,
|
|
6471
|
-
|
|
6472
|
-
|
|
7525
|
+
...parsed,
|
|
7526
|
+
counts: { usersInTenant: 0, workspacesInTenant: 0 }
|
|
7527
|
+
}
|
|
7528
|
+
})
|
|
7529
|
+
});
|
|
7530
|
+
}
|
|
7531
|
+
|
|
7532
|
+
// src/data/rest-api/routes/control/tenant/tenant-list-route.ts
|
|
7533
|
+
async function listTenantsRoute(req, res) {
|
|
7534
|
+
return handleListRoute({
|
|
7535
|
+
req,
|
|
7536
|
+
res,
|
|
7537
|
+
basePath: BASE_PATH.TENANT,
|
|
7538
|
+
listOperation: listTenantsOperation,
|
|
7539
|
+
errorLogContext: "GET /Tenant list error:"
|
|
7540
|
+
});
|
|
7541
|
+
}
|
|
7542
|
+
|
|
7543
|
+
// src/data/operations/control/workspace/workspace-list-operation.ts
|
|
7544
|
+
var SK10 = "CURRENT";
|
|
7545
|
+
function counterValue2(value) {
|
|
7546
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
7547
|
+
}
|
|
7548
|
+
async function listWorkspacesOperation(params) {
|
|
7549
|
+
const { context, tableName, mode = "full" } = params;
|
|
7550
|
+
const { tenantId } = context;
|
|
7551
|
+
const service = getDynamoControlService(tableName);
|
|
7552
|
+
const shardResults = await Promise.all(
|
|
7553
|
+
Array.from(
|
|
7554
|
+
{ length: SHARD_COUNT },
|
|
7555
|
+
(_, shard) => service.entities.workspace.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
|
|
7556
|
+
)
|
|
7557
|
+
);
|
|
7558
|
+
return dispatchListMode(mode, shardResults, {
|
|
7559
|
+
hydrate: (orderedIds) => batchGetWithRetry(
|
|
7560
|
+
service.entities.workspace,
|
|
7561
|
+
orderedIds.map((id) => ({ tenantId, id, sk: SK10 }))
|
|
7562
|
+
),
|
|
7563
|
+
getId: (item) => item.id,
|
|
7564
|
+
// FULL mode (admin list default): read the ADR-028 counters off the
|
|
7565
|
+
// canonical record hydrated by BatchGet and expose them as
|
|
7566
|
+
// `resource.counts`. Missing counters render as 0.
|
|
7567
|
+
buildEntry: (id, item) => ({
|
|
7568
|
+
id,
|
|
7569
|
+
resource: {
|
|
7570
|
+
resourceType: "Workspace",
|
|
7571
|
+
id,
|
|
7572
|
+
...JSON.parse(item.resource),
|
|
7573
|
+
counts: {
|
|
7574
|
+
usersInWorkspace: counterValue2(item.usersInWorkspace),
|
|
7575
|
+
adminUsersInWorkspace: counterValue2(item.adminUsersInWorkspace),
|
|
7576
|
+
normalUsersInWorkspace: counterValue2(item.normalUsersInWorkspace)
|
|
7577
|
+
}
|
|
7578
|
+
}
|
|
7579
|
+
}),
|
|
7580
|
+
// SUMMARY mode reads only the GSI1 `summary` projection (no
|
|
7581
|
+
// counters); surface zeros so the shape stays uniform.
|
|
7582
|
+
buildSummaryEntry: (id, parsed) => ({
|
|
7583
|
+
id,
|
|
7584
|
+
resource: {
|
|
7585
|
+
resourceType: "Workspace",
|
|
7586
|
+
id,
|
|
7587
|
+
...parsed,
|
|
7588
|
+
counts: {
|
|
7589
|
+
usersInWorkspace: 0,
|
|
7590
|
+
adminUsersInWorkspace: 0,
|
|
7591
|
+
normalUsersInWorkspace: 0
|
|
7592
|
+
}
|
|
7593
|
+
}
|
|
7594
|
+
})
|
|
7595
|
+
});
|
|
7596
|
+
}
|
|
7597
|
+
|
|
7598
|
+
// src/data/operations/control/tenant/tenant-users-operation.ts
|
|
7599
|
+
var USER_SK = "CURRENT";
|
|
7600
|
+
function extractEmail(user) {
|
|
7601
|
+
const telecom = user.telecom;
|
|
7602
|
+
if (!Array.isArray(telecom)) {
|
|
7603
|
+
return null;
|
|
7604
|
+
}
|
|
7605
|
+
for (const entry of telecom) {
|
|
7606
|
+
if (entry.system === "email" && typeof entry.value === "string" && entry.value.length > 0) {
|
|
7607
|
+
return entry.value;
|
|
7608
|
+
}
|
|
7609
|
+
}
|
|
7610
|
+
return null;
|
|
7611
|
+
}
|
|
7612
|
+
function extractDisplayName(user) {
|
|
7613
|
+
const name = user.name;
|
|
7614
|
+
if (!Array.isArray(name) || name.length === 0) {
|
|
7615
|
+
return null;
|
|
7616
|
+
}
|
|
7617
|
+
const first = name[0];
|
|
7618
|
+
if (typeof first.text === "string" && first.text.trim().length > 0) {
|
|
7619
|
+
return first.text.trim();
|
|
7620
|
+
}
|
|
7621
|
+
const given = Array.isArray(first.given) ? first.given.filter((g) => typeof g === "string").join(" ").trim() : "";
|
|
7622
|
+
const family = typeof first.family === "string" ? first.family : "";
|
|
7623
|
+
const composed = `${given} ${family}`.trim();
|
|
7624
|
+
return composed.length > 0 ? composed : null;
|
|
7625
|
+
}
|
|
7626
|
+
function extractReferenceDisplay(resource, fieldName) {
|
|
7627
|
+
const field = resource[fieldName];
|
|
7628
|
+
if (!field || typeof field !== "object") {
|
|
7629
|
+
return null;
|
|
7630
|
+
}
|
|
7631
|
+
const display = field.display;
|
|
7632
|
+
if (typeof display !== "string") {
|
|
7633
|
+
return null;
|
|
7634
|
+
}
|
|
7635
|
+
const trimmed = display.trim();
|
|
7636
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
7637
|
+
}
|
|
7638
|
+
function ensureAccumulator(byUser, userId) {
|
|
7639
|
+
let acc = byUser.get(userId);
|
|
7640
|
+
if (!acc) {
|
|
7641
|
+
acc = { tenantRole: null, workspaces: /* @__PURE__ */ new Map() };
|
|
7642
|
+
byUser.set(userId, acc);
|
|
7643
|
+
}
|
|
7644
|
+
return acc;
|
|
7645
|
+
}
|
|
7646
|
+
async function tenantUsersOperation(params) {
|
|
7647
|
+
const { context, tableName } = params;
|
|
7648
|
+
const service = getDynamoControlService(tableName);
|
|
7649
|
+
const [memberships, roleAssignments, workspaces] = await Promise.all([
|
|
7650
|
+
listMembershipsOperation({ context, tableName }),
|
|
7651
|
+
listRoleAssignmentsOperation({ context, tableName }),
|
|
7652
|
+
listWorkspacesOperation({ context, tableName })
|
|
7653
|
+
]);
|
|
7654
|
+
const workspaceNames = /* @__PURE__ */ new Map();
|
|
7655
|
+
for (const entry of workspaces.entries) {
|
|
7656
|
+
const resource = entry.resource;
|
|
7657
|
+
const name = typeof resource.name === "string" && resource.name.trim().length > 0 ? resource.name.trim() : null;
|
|
7658
|
+
workspaceNames.set(entry.id, name);
|
|
7659
|
+
}
|
|
7660
|
+
const byUser = /* @__PURE__ */ new Map();
|
|
7661
|
+
for (const entry of memberships.entries) {
|
|
7662
|
+
const resource = entry.resource;
|
|
7663
|
+
const userId = extractReferenceSlug(resource, "user");
|
|
7664
|
+
if (userId === void 0) {
|
|
7665
|
+
continue;
|
|
7666
|
+
}
|
|
7667
|
+
const acc = ensureAccumulator(byUser, userId);
|
|
7668
|
+
const workspaceId = extractReferenceSlug(resource, "workspace");
|
|
7669
|
+
if (workspaceId !== void 0 && !acc.workspaces.has(workspaceId)) {
|
|
7670
|
+
acc.workspaces.set(workspaceId, { workspaceId, role: null });
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7673
|
+
for (const entry of roleAssignments.entries) {
|
|
7674
|
+
const resource = entry.resource;
|
|
7675
|
+
const userId = extractReferenceSlug(resource, "user");
|
|
7676
|
+
const roleId = extractReferenceSlug(resource, "role");
|
|
7677
|
+
if (userId === void 0 || roleId === void 0) {
|
|
7678
|
+
continue;
|
|
6473
7679
|
}
|
|
7680
|
+
const acc = ensureAccumulator(byUser, userId);
|
|
7681
|
+
const role = {
|
|
7682
|
+
roleId,
|
|
7683
|
+
roleName: extractReferenceDisplay(resource, "role")
|
|
7684
|
+
};
|
|
7685
|
+
const workspaceId = extractReferenceSlug(resource, "workspace");
|
|
7686
|
+
if (workspaceId === void 0) {
|
|
7687
|
+
acc.tenantRole = role;
|
|
7688
|
+
} else {
|
|
7689
|
+
const existing = acc.workspaces.get(workspaceId);
|
|
7690
|
+
if (existing) {
|
|
7691
|
+
existing.role = role;
|
|
7692
|
+
} else {
|
|
7693
|
+
acc.workspaces.set(workspaceId, { workspaceId, role });
|
|
7694
|
+
}
|
|
7695
|
+
}
|
|
7696
|
+
}
|
|
7697
|
+
const userIds = Array.from(byUser.keys());
|
|
7698
|
+
const userRows = userIds.length === 0 ? [] : await batchGetWithRetry(
|
|
7699
|
+
service.entities.user,
|
|
7700
|
+
userIds.map((id) => ({ id, sk: USER_SK }))
|
|
6474
7701
|
);
|
|
7702
|
+
const usersById = /* @__PURE__ */ new Map();
|
|
7703
|
+
for (const row of userRows) {
|
|
7704
|
+
try {
|
|
7705
|
+
usersById.set(
|
|
7706
|
+
row.id,
|
|
7707
|
+
JSON.parse(row.resource)
|
|
7708
|
+
);
|
|
7709
|
+
} catch {
|
|
7710
|
+
}
|
|
7711
|
+
}
|
|
7712
|
+
const entries = userIds.map((userId) => {
|
|
7713
|
+
const acc = byUser.get(userId);
|
|
7714
|
+
const user = usersById.get(userId);
|
|
7715
|
+
const workspaceEntries = Array.from(
|
|
7716
|
+
acc.workspaces.values()
|
|
7717
|
+
).map((ws) => ({
|
|
7718
|
+
workspaceId: ws.workspaceId,
|
|
7719
|
+
workspaceName: workspaceNames.get(ws.workspaceId) ?? null,
|
|
7720
|
+
role: ws.role
|
|
7721
|
+
}));
|
|
7722
|
+
return {
|
|
7723
|
+
resourceType: "TenantUser",
|
|
7724
|
+
userId,
|
|
7725
|
+
displayName: user ? extractDisplayName(user) : null,
|
|
7726
|
+
email: user ? extractEmail(user) : null,
|
|
7727
|
+
tenantRole: acc.tenantRole,
|
|
7728
|
+
workspaces: workspaceEntries
|
|
7729
|
+
};
|
|
7730
|
+
});
|
|
7731
|
+
return { entries };
|
|
6475
7732
|
}
|
|
6476
7733
|
|
|
6477
|
-
// src/data/rest-api/routes/control/tenant/tenant-list-route.ts
|
|
6478
|
-
async function
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
}
|
|
7734
|
+
// src/data/rest-api/routes/control/tenant/tenant-list-users-route.ts
|
|
7735
|
+
async function listTenantUsersRoute(req, res) {
|
|
7736
|
+
const ctx = req.openhiContext;
|
|
7737
|
+
if (!ctx) {
|
|
7738
|
+
return sendForbidden403(
|
|
7739
|
+
res,
|
|
7740
|
+
"Missing or invalid OpenHI JWT claims (tenant, workspace, or audit context)."
|
|
7741
|
+
);
|
|
7742
|
+
}
|
|
7743
|
+
const tenantId = String(req.params.id);
|
|
7744
|
+
if (tenantId.length === 0) {
|
|
7745
|
+
return sendForbidden403(res, "Tenant id is required.");
|
|
7746
|
+
}
|
|
7747
|
+
const parsed = parseCommonListQuery(req, res);
|
|
7748
|
+
if (!parsed.ok) {
|
|
7749
|
+
return parsed.response;
|
|
7750
|
+
}
|
|
7751
|
+
const scopedContext = { ...ctx, tenantId };
|
|
7752
|
+
try {
|
|
7753
|
+
const result = await tenantUsersOperation({ context: scopedContext });
|
|
7754
|
+
const basePath = `${BASE_PATH.TENANT}/${tenantId}/users`;
|
|
7755
|
+
const entries = result.entries.map((entry) => ({
|
|
7756
|
+
fullUrl: `${BASE_PATH.USER}/${entry.userId}`,
|
|
7757
|
+
resource: entry
|
|
7758
|
+
}));
|
|
7759
|
+
return res.json({
|
|
7760
|
+
resourceType: "Bundle",
|
|
7761
|
+
type: "searchset",
|
|
7762
|
+
total: entries.length,
|
|
7763
|
+
link: [{ relation: "self", url: basePath }],
|
|
7764
|
+
entry: entries
|
|
7765
|
+
});
|
|
7766
|
+
} catch (err) {
|
|
7767
|
+
return sendOperationOutcome500(res, err, "GET /Tenant/:id/users error:");
|
|
7768
|
+
}
|
|
6486
7769
|
}
|
|
6487
7770
|
|
|
6488
7771
|
// src/data/operations/control/tenant/tenant-update-operation.ts
|
|
@@ -6552,6 +7835,7 @@ async function updateTenantRoute(req, res) {
|
|
|
6552
7835
|
// src/data/rest-api/routes/control/tenant/tenant.ts
|
|
6553
7836
|
var router6 = import_express6.default.Router();
|
|
6554
7837
|
router6.get("/", listTenantsRoute);
|
|
7838
|
+
router6.get("/:id/users", listTenantUsersRoute);
|
|
6555
7839
|
router6.get("/:id", getTenantByIdRoute);
|
|
6556
7840
|
router6.post("/", createTenantRoute);
|
|
6557
7841
|
router6.put("/:id", updateTenantRoute);
|
|
@@ -6747,125 +8031,6 @@ async function configurationListByWorkspaceOperation(params) {
|
|
|
6747
8031
|
return { items, cursor: result.cursor ?? null };
|
|
6748
8032
|
}
|
|
6749
8033
|
|
|
6750
|
-
// src/data/rest-api/routes/control/cross-cutting-route-helpers.ts
|
|
6751
|
-
function sendInvalidQuery400(res, diagnostics) {
|
|
6752
|
-
return res.status(400).json({
|
|
6753
|
-
resourceType: "OperationOutcome",
|
|
6754
|
-
issue: [{ severity: "error", code: "invalid", diagnostics }]
|
|
6755
|
-
});
|
|
6756
|
-
}
|
|
6757
|
-
function sendForbidden403(res, diagnostics) {
|
|
6758
|
-
return res.status(403).json({
|
|
6759
|
-
resourceType: "OperationOutcome",
|
|
6760
|
-
issue: [{ severity: "error", code: "forbidden", diagnostics }]
|
|
6761
|
-
});
|
|
6762
|
-
}
|
|
6763
|
-
var COMMON_KEYS = ["cursor", "limit", "order"];
|
|
6764
|
-
function parseCommonListQuery(req, res, options = {}) {
|
|
6765
|
-
const q = req.query ?? {};
|
|
6766
|
-
const allowedKeys = /* @__PURE__ */ new Set([
|
|
6767
|
-
...COMMON_KEYS,
|
|
6768
|
-
...options.extraKeys ?? []
|
|
6769
|
-
]);
|
|
6770
|
-
const extra = Object.keys(q).filter((k) => !allowedKeys.has(k));
|
|
6771
|
-
if (extra.length > 0) {
|
|
6772
|
-
return {
|
|
6773
|
-
ok: false,
|
|
6774
|
-
response: sendInvalidQuery400(
|
|
6775
|
-
res,
|
|
6776
|
-
`Unsupported query parameter${extra.length === 1 ? "" : "s"}: ${extra.join(", ")}.`
|
|
6777
|
-
)
|
|
6778
|
-
};
|
|
6779
|
-
}
|
|
6780
|
-
const rawCursor = q.cursor;
|
|
6781
|
-
let cursor = null;
|
|
6782
|
-
if (rawCursor !== void 0) {
|
|
6783
|
-
if (typeof rawCursor !== "string") {
|
|
6784
|
-
return {
|
|
6785
|
-
ok: false,
|
|
6786
|
-
response: sendInvalidQuery400(
|
|
6787
|
-
res,
|
|
6788
|
-
"Query parameter `cursor` must be a string."
|
|
6789
|
-
)
|
|
6790
|
-
};
|
|
6791
|
-
}
|
|
6792
|
-
cursor = rawCursor;
|
|
6793
|
-
}
|
|
6794
|
-
const rawLimit = q.limit;
|
|
6795
|
-
let limit;
|
|
6796
|
-
if (rawLimit !== void 0) {
|
|
6797
|
-
if (typeof rawLimit !== "string" || rawLimit.length === 0) {
|
|
6798
|
-
return {
|
|
6799
|
-
ok: false,
|
|
6800
|
-
response: sendInvalidQuery400(
|
|
6801
|
-
res,
|
|
6802
|
-
"Query parameter `limit` must be a positive integer."
|
|
6803
|
-
)
|
|
6804
|
-
};
|
|
6805
|
-
}
|
|
6806
|
-
const parsed = Number(rawLimit);
|
|
6807
|
-
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
6808
|
-
return {
|
|
6809
|
-
ok: false,
|
|
6810
|
-
response: sendInvalidQuery400(
|
|
6811
|
-
res,
|
|
6812
|
-
"Query parameter `limit` must be a positive integer."
|
|
6813
|
-
)
|
|
6814
|
-
};
|
|
6815
|
-
}
|
|
6816
|
-
limit = parsed;
|
|
6817
|
-
}
|
|
6818
|
-
const rawOrder = q.order;
|
|
6819
|
-
let order;
|
|
6820
|
-
if (rawOrder !== void 0) {
|
|
6821
|
-
if (rawOrder !== "asc" && rawOrder !== "desc") {
|
|
6822
|
-
return {
|
|
6823
|
-
ok: false,
|
|
6824
|
-
response: sendInvalidQuery400(
|
|
6825
|
-
res,
|
|
6826
|
-
'Query parameter `order` must be one of "asc", "desc".'
|
|
6827
|
-
)
|
|
6828
|
-
};
|
|
6829
|
-
}
|
|
6830
|
-
order = rawOrder;
|
|
6831
|
-
}
|
|
6832
|
-
const value = order === void 0 ? limit === void 0 ? { cursor } : { cursor, limit } : limit === void 0 ? { cursor, order } : { cursor, limit, order };
|
|
6833
|
-
return { ok: true, value };
|
|
6834
|
-
}
|
|
6835
|
-
function buildPaginationLinks(opts) {
|
|
6836
|
-
const links = [
|
|
6837
|
-
{ relation: "self", url: composeUrl(opts.basePath, opts.query) }
|
|
6838
|
-
];
|
|
6839
|
-
if (opts.nextCursor !== null && opts.nextCursor.length > 0) {
|
|
6840
|
-
const nextQuery = { ...opts.query };
|
|
6841
|
-
nextQuery.cursor = opts.nextCursor;
|
|
6842
|
-
links.push({
|
|
6843
|
-
relation: "next",
|
|
6844
|
-
url: composeUrl(opts.basePath, nextQuery)
|
|
6845
|
-
});
|
|
6846
|
-
}
|
|
6847
|
-
return links;
|
|
6848
|
-
}
|
|
6849
|
-
function composeUrl(basePath, query) {
|
|
6850
|
-
const usp = new URLSearchParams();
|
|
6851
|
-
for (const [key, value] of Object.entries(query)) {
|
|
6852
|
-
if (value === void 0 || value === null) {
|
|
6853
|
-
continue;
|
|
6854
|
-
}
|
|
6855
|
-
if (Array.isArray(value)) {
|
|
6856
|
-
for (const v of value) {
|
|
6857
|
-
if (v !== void 0 && v !== null) {
|
|
6858
|
-
usp.append(key, String(v));
|
|
6859
|
-
}
|
|
6860
|
-
}
|
|
6861
|
-
} else {
|
|
6862
|
-
usp.append(key, String(value));
|
|
6863
|
-
}
|
|
6864
|
-
}
|
|
6865
|
-
const qs = usp.toString();
|
|
6866
|
-
return qs.length === 0 ? basePath : `${basePath}?${qs}`;
|
|
6867
|
-
}
|
|
6868
|
-
|
|
6869
8034
|
// src/data/rest-api/routes/control/projection-bundle-helpers.ts
|
|
6870
8035
|
var EXT_BASE = "https://openhi.org/fhir/StructureDefinition";
|
|
6871
8036
|
function parseProjectionSummary(summary) {
|
|
@@ -7181,6 +8346,20 @@ async function membershipListByUserOperation(params) {
|
|
|
7181
8346
|
return { items, cursor: result.cursor ?? null };
|
|
7182
8347
|
}
|
|
7183
8348
|
|
|
8349
|
+
// src/data/operations/control/membership/membership-count-by-user-operation.ts
|
|
8350
|
+
async function countMembershipsByUserOperation(params) {
|
|
8351
|
+
const { userId, mode = "all", tenantId, tableName } = params;
|
|
8352
|
+
if (mode === "workspaceInTenant" && !tenantId) {
|
|
8353
|
+
throw new Error(
|
|
8354
|
+
'countMembershipsByUserOperation: tenantId is required when mode === "workspaceInTenant"'
|
|
8355
|
+
);
|
|
8356
|
+
}
|
|
8357
|
+
const service = getDynamoControlService(tableName);
|
|
8358
|
+
const skPrefix = buildSkPrefix(mode, tenantId);
|
|
8359
|
+
const result = await service.entities.membershipUserProjection.query.record({ userId }).begins({ sk: skPrefix }).go({ pages: "all", attributes: ["membershipId"] });
|
|
8360
|
+
return (result.data ?? []).length;
|
|
8361
|
+
}
|
|
8362
|
+
|
|
7184
8363
|
// src/data/operations/control/membership/membership-list-by-workspace-operation.ts
|
|
7185
8364
|
async function membershipListByWorkspaceOperation(params) {
|
|
7186
8365
|
const {
|
|
@@ -7239,7 +8418,7 @@ async function listUserMembershipsRoute(req, res) {
|
|
|
7239
8418
|
);
|
|
7240
8419
|
}
|
|
7241
8420
|
const parsed = parseCommonListQuery(req, res, {
|
|
7242
|
-
extraKeys: ["mode", "tenantId"]
|
|
8421
|
+
extraKeys: ["mode", "tenantId", "_summary"]
|
|
7243
8422
|
});
|
|
7244
8423
|
if (!parsed.ok) {
|
|
7245
8424
|
return parsed.response;
|
|
@@ -7272,6 +8451,33 @@ async function listUserMembershipsRoute(req, res) {
|
|
|
7272
8451
|
'Query parameter `tenantId` is required when `mode === "workspaceInTenant"`.'
|
|
7273
8452
|
);
|
|
7274
8453
|
}
|
|
8454
|
+
const rawSummary = req.query._summary;
|
|
8455
|
+
if (rawSummary !== void 0) {
|
|
8456
|
+
if (rawSummary !== "count") {
|
|
8457
|
+
return sendInvalidQuery400(
|
|
8458
|
+
res,
|
|
8459
|
+
'Query parameter `_summary` must be "count" on this endpoint.'
|
|
8460
|
+
);
|
|
8461
|
+
}
|
|
8462
|
+
try {
|
|
8463
|
+
const total = await countMembershipsByUserOperation({
|
|
8464
|
+
userId,
|
|
8465
|
+
mode,
|
|
8466
|
+
tenantId
|
|
8467
|
+
});
|
|
8468
|
+
return res.json({
|
|
8469
|
+
resourceType: "Bundle",
|
|
8470
|
+
type: "searchset",
|
|
8471
|
+
total
|
|
8472
|
+
});
|
|
8473
|
+
} catch (err) {
|
|
8474
|
+
return sendOperationOutcome500(
|
|
8475
|
+
res,
|
|
8476
|
+
err,
|
|
8477
|
+
"GET /User/:id/Membership count error:"
|
|
8478
|
+
);
|
|
8479
|
+
}
|
|
8480
|
+
}
|
|
7275
8481
|
try {
|
|
7276
8482
|
const result = await membershipListByUserOperation({
|
|
7277
8483
|
userId,
|
|
@@ -7504,7 +8710,10 @@ async function listUserRoleAssignmentsRoute(req, res) {
|
|
|
7504
8710
|
}
|
|
7505
8711
|
|
|
7506
8712
|
// src/data/operations/control/user/user-list-operation.ts
|
|
7507
|
-
var
|
|
8713
|
+
var SK11 = "CURRENT";
|
|
8714
|
+
function counterValue3(value) {
|
|
8715
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
8716
|
+
}
|
|
7508
8717
|
async function listUsersOperation(params) {
|
|
7509
8718
|
const { tableName, mode = "full" } = params;
|
|
7510
8719
|
const service = getDynamoControlService(tableName);
|
|
@@ -7517,20 +8726,34 @@ async function listUsersOperation(params) {
|
|
|
7517
8726
|
return dispatchListMode(mode, shardResults, {
|
|
7518
8727
|
hydrate: (orderedIds) => batchGetWithRetry(
|
|
7519
8728
|
service.entities.user,
|
|
7520
|
-
orderedIds.map((id) => ({ id, sk:
|
|
8729
|
+
orderedIds.map((id) => ({ id, sk: SK11 }))
|
|
7521
8730
|
),
|
|
7522
8731
|
getId: (item) => item.id,
|
|
8732
|
+
// FULL mode (admin list default): read the ADR-028 counters off the
|
|
8733
|
+
// canonical record hydrated by BatchGet and expose them as
|
|
8734
|
+
// `resource.counts`. Missing counters render as 0.
|
|
7523
8735
|
buildEntry: (id, item) => ({
|
|
7524
8736
|
id,
|
|
7525
8737
|
resource: {
|
|
7526
8738
|
resourceType: "User",
|
|
7527
8739
|
id,
|
|
7528
|
-
...JSON.parse(item.resource)
|
|
8740
|
+
...JSON.parse(item.resource),
|
|
8741
|
+
counts: {
|
|
8742
|
+
tenantsForUser: counterValue3(item.tenantsForUser),
|
|
8743
|
+
workspacesForUser: counterValue3(item.workspacesForUser)
|
|
8744
|
+
}
|
|
7529
8745
|
}
|
|
7530
8746
|
}),
|
|
8747
|
+
// SUMMARY mode reads only the GSI1 `summary` projection (no
|
|
8748
|
+
// counters); surface zeros so the shape stays uniform.
|
|
7531
8749
|
buildSummaryEntry: (id, parsed) => ({
|
|
7532
8750
|
id,
|
|
7533
|
-
resource: {
|
|
8751
|
+
resource: {
|
|
8752
|
+
resourceType: "User",
|
|
8753
|
+
id,
|
|
8754
|
+
...parsed,
|
|
8755
|
+
counts: { tenantsForUser: 0, workspacesForUser: 0 }
|
|
8756
|
+
}
|
|
7534
8757
|
})
|
|
7535
8758
|
});
|
|
7536
8759
|
}
|
|
@@ -7687,7 +8910,7 @@ function idFromReference(reference, prefix) {
|
|
|
7687
8910
|
}
|
|
7688
8911
|
|
|
7689
8912
|
// src/data/operations/control/user/user-switch-tenant-workspace-operation.ts
|
|
7690
|
-
var
|
|
8913
|
+
var SK12 = "CURRENT";
|
|
7691
8914
|
async function switchUserTenantWorkspaceOperation(params) {
|
|
7692
8915
|
const { cognitoSub, tenantReference, workspaceReference, tableName } = params;
|
|
7693
8916
|
const tenantId = idFromReference(tenantReference, "Tenant/");
|
|
@@ -7748,7 +8971,7 @@ async function switchUserTenantWorkspaceOperation(params) {
|
|
|
7748
8971
|
(0, import_types20.extractSummary)(updatedResource)
|
|
7749
8972
|
);
|
|
7750
8973
|
const service = getDynamoControlService(tableName);
|
|
7751
|
-
await service.entities.user.patch({ id: user.id, sk:
|
|
8974
|
+
await service.entities.user.patch({ id: user.id, sk: SK12 }).set({
|
|
7752
8975
|
resource: JSON.stringify(updatedResource),
|
|
7753
8976
|
summary,
|
|
7754
8977
|
vid,
|
|
@@ -8227,6 +9450,10 @@ async function createWorkspaceOperation(params) {
|
|
|
8227
9450
|
workspaceName,
|
|
8228
9451
|
tableName
|
|
8229
9452
|
});
|
|
9453
|
+
await publishWorkspaceCreated(context, {
|
|
9454
|
+
workspaceId: id,
|
|
9455
|
+
tenantId
|
|
9456
|
+
});
|
|
8230
9457
|
return { id, resource, meta: { lastUpdated, versionId: vid } };
|
|
8231
9458
|
}
|
|
8232
9459
|
|
|
@@ -8275,12 +9502,20 @@ async function deleteWorkspaceOperation(params) {
|
|
|
8275
9502
|
const { context, id, tableName } = params;
|
|
8276
9503
|
const { tenantId } = context;
|
|
8277
9504
|
const service = getDynamoControlService(tableName);
|
|
9505
|
+
const existing = await service.entities.workspace.get({ tenantId, id, sk: "CURRENT" }).go();
|
|
9506
|
+
const workspaceExisted = existing.data !== null;
|
|
8278
9507
|
await service.entities.workspace.delete({ tenantId, id, sk: "CURRENT" }).go();
|
|
8279
9508
|
await deleteOrganizationOperation({
|
|
8280
9509
|
context: { ...context, workspaceId: id },
|
|
8281
9510
|
id,
|
|
8282
9511
|
tableName
|
|
8283
9512
|
});
|
|
9513
|
+
if (workspaceExisted) {
|
|
9514
|
+
await publishWorkspaceDeleted(context, {
|
|
9515
|
+
workspaceId: id,
|
|
9516
|
+
tenantId
|
|
9517
|
+
});
|
|
9518
|
+
}
|
|
8284
9519
|
}
|
|
8285
9520
|
|
|
8286
9521
|
// src/data/rest-api/routes/control/workspace/workspace-delete-route.ts
|
|
@@ -8546,51 +9781,19 @@ async function listWorkspaceRoleAssignmentsRoute(req, res) {
|
|
|
8546
9781
|
}
|
|
8547
9782
|
}
|
|
8548
9783
|
|
|
8549
|
-
// src/data/operations/control/workspace/workspace-list-operation.ts
|
|
8550
|
-
var SK12 = "CURRENT";
|
|
8551
|
-
async function listWorkspacesOperation(params) {
|
|
8552
|
-
const { context, tableName, mode = "full" } = params;
|
|
8553
|
-
const { tenantId } = context;
|
|
8554
|
-
const service = getDynamoControlService(tableName);
|
|
8555
|
-
const shardResults = await Promise.all(
|
|
8556
|
-
Array.from(
|
|
8557
|
-
{ length: SHARD_COUNT },
|
|
8558
|
-
(_, shard) => service.entities.workspace.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
|
|
8559
|
-
)
|
|
8560
|
-
);
|
|
8561
|
-
return dispatchListMode(
|
|
8562
|
-
mode,
|
|
8563
|
-
shardResults,
|
|
8564
|
-
{
|
|
8565
|
-
hydrate: (orderedIds) => batchGetWithRetry(
|
|
8566
|
-
service.entities.workspace,
|
|
8567
|
-
orderedIds.map((id) => ({ tenantId, id, sk: SK12 }))
|
|
8568
|
-
),
|
|
8569
|
-
getId: (item) => item.id,
|
|
8570
|
-
buildEntry: (id, item) => ({
|
|
8571
|
-
id,
|
|
8572
|
-
resource: {
|
|
8573
|
-
resourceType: "Workspace",
|
|
8574
|
-
id,
|
|
8575
|
-
...JSON.parse(item.resource)
|
|
8576
|
-
}
|
|
8577
|
-
}),
|
|
8578
|
-
buildSummaryEntry: (id, parsed) => ({
|
|
8579
|
-
id,
|
|
8580
|
-
resource: { resourceType: "Workspace", id, ...parsed }
|
|
8581
|
-
})
|
|
8582
|
-
}
|
|
8583
|
-
);
|
|
8584
|
-
}
|
|
8585
|
-
|
|
8586
9784
|
// src/data/rest-api/routes/control/workspace/workspace-list-route.ts
|
|
8587
9785
|
async function listWorkspacesRoute(req, res) {
|
|
9786
|
+
const scope = resolveTenantScopeOverride(req, res, req.openhiContext);
|
|
9787
|
+
if (!scope.ok) {
|
|
9788
|
+
return scope.response;
|
|
9789
|
+
}
|
|
8588
9790
|
return handleListRoute({
|
|
8589
9791
|
req,
|
|
8590
9792
|
res,
|
|
8591
9793
|
basePath: BASE_PATH.WORKSPACE,
|
|
8592
9794
|
listOperation: listWorkspacesOperation,
|
|
8593
|
-
errorLogContext: "GET /Workspace list error:"
|
|
9795
|
+
errorLogContext: "GET /Workspace list error:",
|
|
9796
|
+
context: scope.context
|
|
8594
9797
|
});
|
|
8595
9798
|
}
|
|
8596
9799
|
|