@openhi/constructs 0.0.105 → 0.0.107
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-36UPY7YQ.mjs +529 -0
- package/lib/chunk-36UPY7YQ.mjs.map +1 -0
- package/lib/chunk-AGF3RAAZ.mjs +20 -0
- package/lib/chunk-AGF3RAAZ.mjs.map +1 -0
- package/lib/{chunk-BXEG7IOZ.mjs → chunk-AO3E22CS.mjs} +2 -2
- package/lib/{chunk-WNUH2WDZ.mjs → chunk-CHPEQRXU.mjs} +2 -2
- package/lib/chunk-JUNL76HF.mjs +428 -0
- package/lib/chunk-JUNL76HF.mjs.map +1 -0
- package/lib/chunk-L6UAP4KP.mjs +27 -0
- package/lib/chunk-L6UAP4KP.mjs.map +1 -0
- package/lib/{chunk-3QS3WKRC.mjs → chunk-LZOMFHX3.mjs} +9 -2
- package/lib/chunk-SYBADQXI.mjs +607 -0
- package/lib/chunk-SYBADQXI.mjs.map +1 -0
- package/lib/chunk-VXX4I3EF.mjs +19 -0
- package/lib/chunk-VXX4I3EF.mjs.map +1 -0
- package/lib/{chunk-36YCDLLA.mjs → chunk-VYDIGFIX.mjs} +75 -481
- package/lib/chunk-VYDIGFIX.mjs.map +1 -0
- package/lib/chunk-YU2HRNUP.mjs +33 -0
- package/lib/chunk-YU2HRNUP.mjs.map +1 -0
- package/lib/chunk-YZZDUJHI.mjs +37 -0
- package/lib/chunk-YZZDUJHI.mjs.map +1 -0
- package/lib/cors-options-lambda.handler.mjs +1 -1
- package/lib/data-store-postgres-replication.handler.mjs +1 -1
- package/lib/events-BfrkMoBD.d.mts +44 -0
- package/lib/events-BfrkMoBD.d.ts +44 -0
- package/lib/events-DPodvl07.d.mts +207 -0
- package/lib/events-DPodvl07.d.ts +207 -0
- package/lib/firehose-archive-transform.handler.mjs +1 -1
- package/lib/index.d.mts +417 -9
- package/lib/index.d.ts +663 -10
- package/lib/index.js +2398 -111
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +779 -104
- package/lib/index.mjs.map +1 -1
- package/lib/openhi-context-CaBH8SFo.d.mts +39 -0
- package/lib/openhi-context-CaBH8SFo.d.ts +39 -0
- package/lib/platform-deploy-bridge.handler.d.mts +14 -0
- package/lib/platform-deploy-bridge.handler.d.ts +14 -0
- package/lib/platform-deploy-bridge.handler.js +762 -0
- package/lib/platform-deploy-bridge.handler.js.map +1 -0
- package/lib/platform-deploy-bridge.handler.mjs +134 -0
- package/lib/platform-deploy-bridge.handler.mjs.map +1 -0
- package/lib/post-authentication.handler.mjs +1 -1
- package/lib/post-confirmation.handler.mjs +1 -1
- package/lib/pre-token-generation.handler.js +76 -31
- package/lib/pre-token-generation.handler.js.map +1 -1
- package/lib/pre-token-generation.handler.mjs +5 -3
- package/lib/pre-token-generation.handler.mjs.map +1 -1
- package/lib/provision-default-workspace.handler.js +86 -41
- package/lib/provision-default-workspace.handler.js.map +1 -1
- package/lib/provision-default-workspace.handler.mjs +6 -4
- package/lib/provision-default-workspace.handler.mjs.map +1 -1
- package/lib/rest-api-lambda.handler.js +114 -59
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +40 -61
- package/lib/rest-api-lambda.handler.mjs.map +1 -1
- package/lib/seed-demo-data.handler.d.mts +107 -0
- package/lib/seed-demo-data.handler.d.ts +107 -0
- package/lib/seed-demo-data.handler.js +2037 -0
- package/lib/seed-demo-data.handler.js.map +1 -0
- package/lib/seed-demo-data.handler.mjs +23 -0
- package/lib/seed-demo-data.handler.mjs.map +1 -0
- package/lib/seed-system-data.handler.d.mts +64 -0
- package/lib/seed-system-data.handler.d.ts +64 -0
- package/lib/seed-system-data.handler.js +1631 -0
- package/lib/seed-system-data.handler.js.map +1 -0
- package/lib/seed-system-data.handler.mjs +135 -0
- package/lib/seed-system-data.handler.mjs.map +1 -0
- package/package.json +4 -2
- package/lib/chunk-36YCDLLA.mjs.map +0 -1
- /package/lib/{chunk-BXEG7IOZ.mjs.map → chunk-AO3E22CS.mjs.map} +0 -0
- /package/lib/{chunk-WNUH2WDZ.mjs.map → chunk-CHPEQRXU.mjs.map} +0 -0
- /package/lib/{chunk-3QS3WKRC.mjs.map → chunk-LZOMFHX3.mjs.map} +0 -0
package/lib/index.js
CHANGED
|
@@ -90,9 +90,611 @@ var require_lib = __commonJS({
|
|
|
90
90
|
}
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
+
// ../workflows/lib/envelope-version.js
|
|
94
|
+
var require_envelope_version = __commonJS({
|
|
95
|
+
"../workflows/lib/envelope-version.js"(exports2) {
|
|
96
|
+
"use strict";
|
|
97
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
98
|
+
exports2.ENVELOPE_VERSION = void 0;
|
|
99
|
+
exports2.isSupportedEnvelopeVersion = isSupportedEnvelopeVersion;
|
|
100
|
+
exports2.ENVELOPE_VERSION = "1.0";
|
|
101
|
+
var ENVELOPE_VERSION_PATTERN = /^\d+\.\d+$/;
|
|
102
|
+
var MIN_SUPPORTED_MAJOR = 1;
|
|
103
|
+
var MAX_SUPPORTED_MAJOR = 1;
|
|
104
|
+
function isSupportedEnvelopeVersion(version) {
|
|
105
|
+
if (!ENVELOPE_VERSION_PATTERN.test(version)) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const major = Number.parseInt(version.split(".")[0], 10);
|
|
109
|
+
return major >= MIN_SUPPORTED_MAJOR && major <= MAX_SUPPORTED_MAJOR;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// ../workflows/lib/envelope.js
|
|
115
|
+
var require_envelope = __commonJS({
|
|
116
|
+
"../workflows/lib/envelope.js"(exports2) {
|
|
117
|
+
"use strict";
|
|
118
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
119
|
+
exports2.MissingActorContextError = void 0;
|
|
120
|
+
exports2.isWorkflowUserActor = isWorkflowUserActor;
|
|
121
|
+
exports2.isWorkflowSystemActor = isWorkflowSystemActor;
|
|
122
|
+
exports2.workflowUserActorFromClaims = workflowUserActorFromClaims;
|
|
123
|
+
function isWorkflowUserActor(actor) {
|
|
124
|
+
return actor.ohi_uid !== void 0;
|
|
125
|
+
}
|
|
126
|
+
function isWorkflowSystemActor(actor) {
|
|
127
|
+
return actor.system !== void 0;
|
|
128
|
+
}
|
|
129
|
+
function workflowUserActorFromClaims(claims) {
|
|
130
|
+
if (claims.ohi_tid === void 0 || claims.ohi_wid === void 0) {
|
|
131
|
+
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.");
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
ohi_tid: claims.ohi_tid,
|
|
135
|
+
ohi_wid: claims.ohi_wid,
|
|
136
|
+
ohi_uid: claims.ohi_uid,
|
|
137
|
+
ohi_uname: claims.ohi_uname
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
var MissingActorContextError = class extends Error {
|
|
141
|
+
/** @param message - human-readable description of the missing claim. */
|
|
142
|
+
constructor(message) {
|
|
143
|
+
super(message);
|
|
144
|
+
this.name = "MissingActorContextError";
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
exports2.MissingActorContextError = MissingActorContextError;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// ../workflows/lib/sources.js
|
|
152
|
+
var require_sources = __commonJS({
|
|
153
|
+
"../workflows/lib/sources.js"(exports2) {
|
|
154
|
+
"use strict";
|
|
155
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
156
|
+
exports2.DEFAULT_BUS_NAME_BY_SOURCE = exports2.OPENHI_OPS_SOURCE = exports2.OPENHI_DATA_SOURCE = exports2.OPENHI_CONTROL_SOURCE = void 0;
|
|
157
|
+
exports2.OPENHI_CONTROL_SOURCE = "openhi.control";
|
|
158
|
+
exports2.OPENHI_DATA_SOURCE = "openhi.data";
|
|
159
|
+
exports2.OPENHI_OPS_SOURCE = "openhi.ops";
|
|
160
|
+
exports2.DEFAULT_BUS_NAME_BY_SOURCE = {
|
|
161
|
+
[exports2.OPENHI_CONTROL_SOURCE]: "openhi-control-event-bus",
|
|
162
|
+
[exports2.OPENHI_DATA_SOURCE]: "openhi-data-event-bus",
|
|
163
|
+
[exports2.OPENHI_OPS_SOURCE]: "openhi-ops-event-bus"
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// ../workflows/lib/detail-types/registry.js
|
|
169
|
+
var require_registry = __commonJS({
|
|
170
|
+
"../workflows/lib/detail-types/registry.js"(exports2) {
|
|
171
|
+
"use strict";
|
|
172
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
173
|
+
exports2.InvalidDetailTypeRegistrationError = void 0;
|
|
174
|
+
exports2.defineDetailType = defineDetailType;
|
|
175
|
+
exports2.isWellFormedDetailType = isWellFormedDetailType;
|
|
176
|
+
function defineDetailType(entry) {
|
|
177
|
+
if (!isWellFormedDetailType(entry.detailType)) {
|
|
178
|
+
throw new InvalidDetailTypeRegistrationError(`Detail-type "${entry.detailType}" does not match the platform-wide format <area>.<event>.v<integer>. See TR-016 \xA7Open Items #2.`);
|
|
179
|
+
}
|
|
180
|
+
return entry;
|
|
181
|
+
}
|
|
182
|
+
var DETAIL_TYPE_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z0-9]+(?:-[a-z0-9]+)*\.v\d+$/;
|
|
183
|
+
function isWellFormedDetailType(detailType) {
|
|
184
|
+
return DETAIL_TYPE_PATTERN.test(detailType);
|
|
185
|
+
}
|
|
186
|
+
var InvalidDetailTypeRegistrationError = class extends Error {
|
|
187
|
+
/** @param message - human-readable description of the violation. */
|
|
188
|
+
constructor(message) {
|
|
189
|
+
super(message);
|
|
190
|
+
this.name = "InvalidDetailTypeRegistrationError";
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
exports2.InvalidDetailTypeRegistrationError = InvalidDetailTypeRegistrationError;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// ../workflows/lib/detail-types/platform.js
|
|
198
|
+
var require_platform = __commonJS({
|
|
199
|
+
"../workflows/lib/detail-types/platform.js"(exports2) {
|
|
200
|
+
"use strict";
|
|
201
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
202
|
+
exports2.PlatformSystemDataSeededV1 = exports2.PlatformDeploymentCompletedV1 = void 0;
|
|
203
|
+
var sources_1 = require_sources();
|
|
204
|
+
var registry_1 = require_registry();
|
|
205
|
+
exports2.PlatformDeploymentCompletedV1 = (0, registry_1.defineDetailType)({
|
|
206
|
+
detailType: "platform.deployment-completed.v1",
|
|
207
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
208
|
+
dedupRequired: true
|
|
209
|
+
});
|
|
210
|
+
exports2.PlatformSystemDataSeededV1 = (0, registry_1.defineDetailType)({
|
|
211
|
+
detailType: "platform.system-data-seeded.v1",
|
|
212
|
+
source: sources_1.OPENHI_CONTROL_SOURCE,
|
|
213
|
+
dedupRequired: true
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// ../workflows/lib/detail-types/index.js
|
|
219
|
+
var require_detail_types = __commonJS({
|
|
220
|
+
"../workflows/lib/detail-types/index.js"(exports2) {
|
|
221
|
+
"use strict";
|
|
222
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
223
|
+
if (k2 === void 0) k2 = k;
|
|
224
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
225
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
226
|
+
desc = { enumerable: true, get: function() {
|
|
227
|
+
return m[k];
|
|
228
|
+
} };
|
|
229
|
+
}
|
|
230
|
+
Object.defineProperty(o, k2, desc);
|
|
231
|
+
}) : (function(o, m, k, k2) {
|
|
232
|
+
if (k2 === void 0) k2 = k;
|
|
233
|
+
o[k2] = m[k];
|
|
234
|
+
}));
|
|
235
|
+
var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
|
|
236
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
|
|
237
|
+
};
|
|
238
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
239
|
+
__exportStar(require_platform(), exports2);
|
|
240
|
+
__exportStar(require_registry(), exports2);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// ../workflows/lib/publisher.js
|
|
245
|
+
var require_publisher = __commonJS({
|
|
246
|
+
"../workflows/lib/publisher.js"(exports2) {
|
|
247
|
+
"use strict";
|
|
248
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
249
|
+
exports2.WorkflowPublishError = void 0;
|
|
250
|
+
exports2.workflowsClient = workflowsClient;
|
|
251
|
+
exports2.publishWorkflowEvent = publishWorkflowEvent;
|
|
252
|
+
var node_crypto_1 = require("crypto");
|
|
253
|
+
var client_eventbridge_1 = require("@aws-sdk/client-eventbridge");
|
|
254
|
+
var envelope_version_1 = require_envelope_version();
|
|
255
|
+
var sources_1 = require_sources();
|
|
256
|
+
function workflowsClient(bridge, options = {}) {
|
|
257
|
+
return {
|
|
258
|
+
publish: (entry, payload, ctx) => publishWorkflowEvent(bridge, entry, payload, ctx, options)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
async function publishWorkflowEvent(bridge, entry, payload, ctx, options = {}) {
|
|
262
|
+
const eventIdGenerator = options.eventIdGenerator ?? (() => (0, node_crypto_1.randomUUID)());
|
|
263
|
+
const correlationIdGenerator = options.correlationIdGenerator ?? (() => (0, node_crypto_1.randomUUID)());
|
|
264
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
265
|
+
const envelope = {
|
|
266
|
+
eventId: eventIdGenerator(),
|
|
267
|
+
attempt: 1,
|
|
268
|
+
correlationId: ctx.correlationId ?? correlationIdGenerator(),
|
|
269
|
+
causationId: ctx.causationId ?? null,
|
|
270
|
+
actor: ctx.actor,
|
|
271
|
+
occurredAt: now().toISOString(),
|
|
272
|
+
envelopeVersion: envelope_version_1.ENVELOPE_VERSION,
|
|
273
|
+
payload
|
|
274
|
+
};
|
|
275
|
+
const busName = options.busNameByPlane?.[entry.source] ?? sources_1.DEFAULT_BUS_NAME_BY_SOURCE[entry.source];
|
|
276
|
+
const result = await bridge.send(new client_eventbridge_1.PutEventsCommand({
|
|
277
|
+
Entries: [
|
|
278
|
+
{
|
|
279
|
+
EventBusName: busName,
|
|
280
|
+
Source: entry.source,
|
|
281
|
+
DetailType: entry.detailType,
|
|
282
|
+
Detail: JSON.stringify(envelope)
|
|
283
|
+
}
|
|
284
|
+
]
|
|
285
|
+
}));
|
|
286
|
+
if ((result.FailedEntryCount ?? 0) > 0) {
|
|
287
|
+
const first = result.Entries?.[0];
|
|
288
|
+
throw new WorkflowPublishError(`EventBridge rejected ${entry.detailType} publish on bus ${busName}: ${first?.ErrorCode ?? "unknown"} \u2014 ${first?.ErrorMessage ?? "no error message"}`);
|
|
289
|
+
}
|
|
290
|
+
return { eventId: envelope.eventId };
|
|
291
|
+
}
|
|
292
|
+
var WorkflowPublishError = class extends Error {
|
|
293
|
+
/** @param message - human-readable description of the failed publish. */
|
|
294
|
+
constructor(message) {
|
|
295
|
+
super(message);
|
|
296
|
+
this.name = "WorkflowPublishError";
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
exports2.WorkflowPublishError = WorkflowPublishError;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// ../workflows/lib/consumer.js
|
|
304
|
+
var require_consumer = __commonJS({
|
|
305
|
+
"../workflows/lib/consumer.js"(exports2) {
|
|
306
|
+
"use strict";
|
|
307
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
308
|
+
exports2.UnsupportedEnvelopeVersionError = exports2.InvalidWorkflowEventError = void 0;
|
|
309
|
+
exports2.parseWorkflowEvent = parseWorkflowEvent2;
|
|
310
|
+
var envelope_version_1 = require_envelope_version();
|
|
311
|
+
function parseWorkflowEvent2(event, expected) {
|
|
312
|
+
if (event.source !== expected.source) {
|
|
313
|
+
throw new InvalidWorkflowEventError(`EventBridge source "${event.source}" does not match expected detail-type's source "${expected.source}".`);
|
|
314
|
+
}
|
|
315
|
+
if (event["detail-type"] !== expected.detailType) {
|
|
316
|
+
throw new InvalidWorkflowEventError(`EventBridge detail-type "${event["detail-type"]}" does not match expected "${expected.detailType}".`);
|
|
317
|
+
}
|
|
318
|
+
const candidate = asEnvelopeCandidate(event.detail);
|
|
319
|
+
if (!(0, envelope_version_1.isSupportedEnvelopeVersion)(candidate.envelopeVersion)) {
|
|
320
|
+
throw new UnsupportedEnvelopeVersionError(`Envelope version "${candidate.envelopeVersion}" is outside the SDK's supported range.`);
|
|
321
|
+
}
|
|
322
|
+
const envelope = {
|
|
323
|
+
eventId: candidate.eventId,
|
|
324
|
+
attempt: candidate.attempt,
|
|
325
|
+
correlationId: candidate.correlationId,
|
|
326
|
+
causationId: candidate.causationId,
|
|
327
|
+
actor: candidate.actor,
|
|
328
|
+
occurredAt: candidate.occurredAt,
|
|
329
|
+
envelopeVersion: candidate.envelopeVersion,
|
|
330
|
+
payload: candidate.payload
|
|
331
|
+
};
|
|
332
|
+
return {
|
|
333
|
+
envelope,
|
|
334
|
+
dedupKey: { eventId: envelope.eventId, attempt: envelope.attempt }
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function asEnvelopeCandidate(detail) {
|
|
338
|
+
if (detail === null || typeof detail !== "object") {
|
|
339
|
+
throw new InvalidWorkflowEventError("EventBridge detail is not a non-null object.");
|
|
340
|
+
}
|
|
341
|
+
const obj = detail;
|
|
342
|
+
assertString(obj, "eventId");
|
|
343
|
+
assertPositiveInteger(obj, "attempt");
|
|
344
|
+
assertString(obj, "correlationId");
|
|
345
|
+
assertCausationId(obj);
|
|
346
|
+
assertActor(obj);
|
|
347
|
+
assertString(obj, "occurredAt");
|
|
348
|
+
assertString(obj, "envelopeVersion");
|
|
349
|
+
if (!("payload" in obj)) {
|
|
350
|
+
throw new InvalidWorkflowEventError("Envelope is missing required field: payload.");
|
|
351
|
+
}
|
|
352
|
+
return obj;
|
|
353
|
+
}
|
|
354
|
+
function assertString(obj, field) {
|
|
355
|
+
const value = obj[field];
|
|
356
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
357
|
+
throw new InvalidWorkflowEventError(`Envelope field "${field}" must be a non-empty string.`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function assertPositiveInteger(obj, field) {
|
|
361
|
+
const value = obj[field];
|
|
362
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
363
|
+
throw new InvalidWorkflowEventError(`Envelope field "${field}" must be a 1-indexed integer.`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function assertCausationId(obj) {
|
|
367
|
+
if (!("causationId" in obj)) {
|
|
368
|
+
throw new InvalidWorkflowEventError("Envelope is missing required field: causationId.");
|
|
369
|
+
}
|
|
370
|
+
const value = obj.causationId;
|
|
371
|
+
if (value !== null && (typeof value !== "string" || value.length === 0)) {
|
|
372
|
+
throw new InvalidWorkflowEventError('Envelope field "causationId" must be a non-empty string or null.');
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function assertActor(obj) {
|
|
376
|
+
const actor = obj.actor;
|
|
377
|
+
if (actor === null || typeof actor !== "object") {
|
|
378
|
+
throw new InvalidWorkflowEventError('Envelope field "actor" must be an object.');
|
|
379
|
+
}
|
|
380
|
+
const actorObj = actor;
|
|
381
|
+
const isUserActor = typeof actorObj.ohi_uid === "string" && typeof actorObj.ohi_uname === "string" && typeof actorObj.ohi_tid === "string" && typeof actorObj.ohi_wid === "string";
|
|
382
|
+
const isSystemActor = typeof actorObj.system === "string";
|
|
383
|
+
if (!isUserActor && !isSystemActor) {
|
|
384
|
+
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 }).');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
var InvalidWorkflowEventError = class extends Error {
|
|
388
|
+
/** @param message - human-readable description of the validation failure. */
|
|
389
|
+
constructor(message) {
|
|
390
|
+
super(message);
|
|
391
|
+
this.name = "InvalidWorkflowEventError";
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
exports2.InvalidWorkflowEventError = InvalidWorkflowEventError;
|
|
395
|
+
var UnsupportedEnvelopeVersionError = class extends Error {
|
|
396
|
+
/** @param message - human-readable description of the unsupported version. */
|
|
397
|
+
constructor(message) {
|
|
398
|
+
super(message);
|
|
399
|
+
this.name = "UnsupportedEnvelopeVersionError";
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
exports2.UnsupportedEnvelopeVersionError = UnsupportedEnvelopeVersionError;
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// ../workflows/lib/dedup/env.js
|
|
407
|
+
var require_env = __commonJS({
|
|
408
|
+
"../workflows/lib/dedup/env.js"(exports2) {
|
|
409
|
+
"use strict";
|
|
410
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
411
|
+
exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = void 0;
|
|
412
|
+
exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = "OPENHI_WORKFLOW_DEDUP_TABLE_NAME";
|
|
413
|
+
exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = 14 * 24 * 60 * 60;
|
|
414
|
+
exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = 64;
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// ../workflows/lib/dedup/workflow-dedup-client.js
|
|
419
|
+
var require_workflow_dedup_client = __commonJS({
|
|
420
|
+
"../workflows/lib/dedup/workflow-dedup-client.js"(exports2) {
|
|
421
|
+
"use strict";
|
|
422
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
423
|
+
exports2.WorkflowDedupInvalidInputError = exports2.WorkflowDedupTableNameMissingError = void 0;
|
|
424
|
+
exports2.workflowDedupClient = workflowDedupClient2;
|
|
425
|
+
exports2.recordIfAbsent = recordIfAbsent;
|
|
426
|
+
exports2.markFailed = markFailed;
|
|
427
|
+
exports2.encodeSortKey = encodeSortKey;
|
|
428
|
+
var client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
429
|
+
var env_1 = require_env();
|
|
430
|
+
function workflowDedupClient2(dynamodb, options = {}) {
|
|
431
|
+
return {
|
|
432
|
+
recordIfAbsent: (input) => recordIfAbsent(dynamodb, input, options),
|
|
433
|
+
markFailed: (input) => markFailed(dynamodb, input, options)
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
async function recordIfAbsent(dynamodb, input, options = {}) {
|
|
437
|
+
assertConsumerName(input.consumerName);
|
|
438
|
+
assertPositiveInteger(input.attempt, "attempt");
|
|
439
|
+
const ttlSeconds = input.ttlSeconds ?? options.defaultTtlSeconds ?? env_1.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;
|
|
440
|
+
if (!Number.isInteger(ttlSeconds) || ttlSeconds <= 0) {
|
|
441
|
+
throw new WorkflowDedupInvalidInputError(`ttlSeconds must be a positive integer; got ${ttlSeconds}.`);
|
|
442
|
+
}
|
|
443
|
+
const tableName = resolveTableName(options.tableName);
|
|
444
|
+
const now = (options.now ?? defaultNow)();
|
|
445
|
+
const sk = encodeSortKey(input.eventId, input.attempt);
|
|
446
|
+
const expiresAt = Math.floor(now.getTime() / 1e3) + ttlSeconds;
|
|
447
|
+
try {
|
|
448
|
+
await dynamodb.send(new client_dynamodb_1.PutItemCommand({
|
|
449
|
+
TableName: tableName,
|
|
450
|
+
Item: {
|
|
451
|
+
consumerName: { S: input.consumerName },
|
|
452
|
+
sk: { S: sk },
|
|
453
|
+
eventId: { S: input.eventId },
|
|
454
|
+
attempt: { N: String(input.attempt) },
|
|
455
|
+
recordedAt: { S: now.toISOString() },
|
|
456
|
+
expiresAt: { N: String(expiresAt) }
|
|
457
|
+
},
|
|
458
|
+
ConditionExpression: "attribute_not_exists(consumerName) AND attribute_not_exists(sk)"
|
|
459
|
+
}));
|
|
460
|
+
return { recorded: true };
|
|
461
|
+
} catch (err) {
|
|
462
|
+
if (err instanceof client_dynamodb_1.ConditionalCheckFailedException) {
|
|
463
|
+
return { recorded: false, alreadyProcessed: true };
|
|
464
|
+
}
|
|
465
|
+
throw err;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async function markFailed(dynamodb, input, options = {}) {
|
|
469
|
+
assertConsumerName(input.consumerName);
|
|
470
|
+
assertPositiveInteger(input.attempt, "attempt");
|
|
471
|
+
if (input.reason.length === 0) {
|
|
472
|
+
throw new WorkflowDedupInvalidInputError("reason must be non-empty.");
|
|
473
|
+
}
|
|
474
|
+
const tableName = resolveTableName(options.tableName);
|
|
475
|
+
const now = (options.now ?? defaultNow)();
|
|
476
|
+
const sk = encodeSortKey(input.eventId, input.attempt);
|
|
477
|
+
await dynamodb.send(new client_dynamodb_1.UpdateItemCommand({
|
|
478
|
+
TableName: tableName,
|
|
479
|
+
Key: {
|
|
480
|
+
consumerName: { S: input.consumerName },
|
|
481
|
+
sk: { S: sk }
|
|
482
|
+
},
|
|
483
|
+
UpdateExpression: "SET #failed = :failed, #failureReason = :reason, #failedAt = :failedAt",
|
|
484
|
+
ExpressionAttributeNames: {
|
|
485
|
+
"#failed": "failed",
|
|
486
|
+
"#failureReason": "failureReason",
|
|
487
|
+
"#failedAt": "failedAt"
|
|
488
|
+
},
|
|
489
|
+
ExpressionAttributeValues: {
|
|
490
|
+
":failed": { BOOL: true },
|
|
491
|
+
":reason": { S: input.reason },
|
|
492
|
+
":failedAt": { S: now.toISOString() }
|
|
493
|
+
}
|
|
494
|
+
}));
|
|
495
|
+
}
|
|
496
|
+
function encodeSortKey(eventId, attempt) {
|
|
497
|
+
if (eventId.length === 0) {
|
|
498
|
+
throw new WorkflowDedupInvalidInputError("eventId must be non-empty.");
|
|
499
|
+
}
|
|
500
|
+
return `${eventId}#${attempt}`;
|
|
501
|
+
}
|
|
502
|
+
function resolveTableName(explicit) {
|
|
503
|
+
const name = explicit ?? process.env[env_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR];
|
|
504
|
+
if (!name) {
|
|
505
|
+
throw new WorkflowDedupTableNameMissingError(`Workflow dedup table name not set. Pass options.tableName or set ${env_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR}.`);
|
|
506
|
+
}
|
|
507
|
+
return name;
|
|
508
|
+
}
|
|
509
|
+
function assertConsumerName(consumerName) {
|
|
510
|
+
if (consumerName.length === 0) {
|
|
511
|
+
throw new WorkflowDedupInvalidInputError("consumerName must be non-empty.");
|
|
512
|
+
}
|
|
513
|
+
if (consumerName.length > env_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH) {
|
|
514
|
+
throw new WorkflowDedupInvalidInputError(`consumerName must be \u2264${env_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH} chars; got ${consumerName.length}.`);
|
|
515
|
+
}
|
|
516
|
+
if (/\s/.test(consumerName)) {
|
|
517
|
+
throw new WorkflowDedupInvalidInputError("consumerName must not contain whitespace.");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function assertPositiveInteger(value, field) {
|
|
521
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
522
|
+
throw new WorkflowDedupInvalidInputError(`${field} must be a 1-indexed integer; got ${value}.`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function defaultNow() {
|
|
526
|
+
return /* @__PURE__ */ new Date();
|
|
527
|
+
}
|
|
528
|
+
var WorkflowDedupTableNameMissingError = class extends Error {
|
|
529
|
+
/** @param message - human-readable description. */
|
|
530
|
+
constructor(message) {
|
|
531
|
+
super(message);
|
|
532
|
+
this.name = "WorkflowDedupTableNameMissingError";
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
exports2.WorkflowDedupTableNameMissingError = WorkflowDedupTableNameMissingError;
|
|
536
|
+
var WorkflowDedupInvalidInputError = class extends Error {
|
|
537
|
+
/** @param message - human-readable description. */
|
|
538
|
+
constructor(message) {
|
|
539
|
+
super(message);
|
|
540
|
+
this.name = "WorkflowDedupInvalidInputError";
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
exports2.WorkflowDedupInvalidInputError = WorkflowDedupInvalidInputError;
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
// ../workflows/lib/dedup/index.js
|
|
548
|
+
var require_dedup = __commonJS({
|
|
549
|
+
"../workflows/lib/dedup/index.js"(exports2) {
|
|
550
|
+
"use strict";
|
|
551
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
552
|
+
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;
|
|
553
|
+
var env_1 = require_env();
|
|
554
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS", { enumerable: true, get: function() {
|
|
555
|
+
return env_1.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;
|
|
556
|
+
} });
|
|
557
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH", { enumerable: true, get: function() {
|
|
558
|
+
return env_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH;
|
|
559
|
+
} });
|
|
560
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR", { enumerable: true, get: function() {
|
|
561
|
+
return env_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR;
|
|
562
|
+
} });
|
|
563
|
+
var workflow_dedup_client_1 = require_workflow_dedup_client();
|
|
564
|
+
Object.defineProperty(exports2, "WorkflowDedupInvalidInputError", { enumerable: true, get: function() {
|
|
565
|
+
return workflow_dedup_client_1.WorkflowDedupInvalidInputError;
|
|
566
|
+
} });
|
|
567
|
+
Object.defineProperty(exports2, "WorkflowDedupTableNameMissingError", { enumerable: true, get: function() {
|
|
568
|
+
return workflow_dedup_client_1.WorkflowDedupTableNameMissingError;
|
|
569
|
+
} });
|
|
570
|
+
Object.defineProperty(exports2, "encodeSortKey", { enumerable: true, get: function() {
|
|
571
|
+
return workflow_dedup_client_1.encodeSortKey;
|
|
572
|
+
} });
|
|
573
|
+
Object.defineProperty(exports2, "markFailed", { enumerable: true, get: function() {
|
|
574
|
+
return workflow_dedup_client_1.markFailed;
|
|
575
|
+
} });
|
|
576
|
+
Object.defineProperty(exports2, "recordIfAbsent", { enumerable: true, get: function() {
|
|
577
|
+
return workflow_dedup_client_1.recordIfAbsent;
|
|
578
|
+
} });
|
|
579
|
+
Object.defineProperty(exports2, "workflowDedupClient", { enumerable: true, get: function() {
|
|
580
|
+
return workflow_dedup_client_1.workflowDedupClient;
|
|
581
|
+
} });
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
// ../workflows/lib/index.js
|
|
586
|
+
var require_lib2 = __commonJS({
|
|
587
|
+
"../workflows/lib/index.js"(exports2) {
|
|
588
|
+
"use strict";
|
|
589
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
590
|
+
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.PlatformSystemDataSeededV1 = exports2.PlatformDeploymentCompletedV1 = exports2.InvalidDetailTypeRegistrationError = 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;
|
|
591
|
+
var envelope_version_1 = require_envelope_version();
|
|
592
|
+
Object.defineProperty(exports2, "ENVELOPE_VERSION", { enumerable: true, get: function() {
|
|
593
|
+
return envelope_version_1.ENVELOPE_VERSION;
|
|
594
|
+
} });
|
|
595
|
+
Object.defineProperty(exports2, "isSupportedEnvelopeVersion", { enumerable: true, get: function() {
|
|
596
|
+
return envelope_version_1.isSupportedEnvelopeVersion;
|
|
597
|
+
} });
|
|
598
|
+
var envelope_1 = require_envelope();
|
|
599
|
+
Object.defineProperty(exports2, "MissingActorContextError", { enumerable: true, get: function() {
|
|
600
|
+
return envelope_1.MissingActorContextError;
|
|
601
|
+
} });
|
|
602
|
+
Object.defineProperty(exports2, "isWorkflowSystemActor", { enumerable: true, get: function() {
|
|
603
|
+
return envelope_1.isWorkflowSystemActor;
|
|
604
|
+
} });
|
|
605
|
+
Object.defineProperty(exports2, "isWorkflowUserActor", { enumerable: true, get: function() {
|
|
606
|
+
return envelope_1.isWorkflowUserActor;
|
|
607
|
+
} });
|
|
608
|
+
Object.defineProperty(exports2, "workflowUserActorFromClaims", { enumerable: true, get: function() {
|
|
609
|
+
return envelope_1.workflowUserActorFromClaims;
|
|
610
|
+
} });
|
|
611
|
+
var sources_1 = require_sources();
|
|
612
|
+
Object.defineProperty(exports2, "DEFAULT_BUS_NAME_BY_SOURCE", { enumerable: true, get: function() {
|
|
613
|
+
return sources_1.DEFAULT_BUS_NAME_BY_SOURCE;
|
|
614
|
+
} });
|
|
615
|
+
Object.defineProperty(exports2, "OPENHI_CONTROL_SOURCE", { enumerable: true, get: function() {
|
|
616
|
+
return sources_1.OPENHI_CONTROL_SOURCE;
|
|
617
|
+
} });
|
|
618
|
+
Object.defineProperty(exports2, "OPENHI_DATA_SOURCE", { enumerable: true, get: function() {
|
|
619
|
+
return sources_1.OPENHI_DATA_SOURCE;
|
|
620
|
+
} });
|
|
621
|
+
Object.defineProperty(exports2, "OPENHI_OPS_SOURCE", { enumerable: true, get: function() {
|
|
622
|
+
return sources_1.OPENHI_OPS_SOURCE;
|
|
623
|
+
} });
|
|
624
|
+
var detail_types_1 = require_detail_types();
|
|
625
|
+
Object.defineProperty(exports2, "InvalidDetailTypeRegistrationError", { enumerable: true, get: function() {
|
|
626
|
+
return detail_types_1.InvalidDetailTypeRegistrationError;
|
|
627
|
+
} });
|
|
628
|
+
Object.defineProperty(exports2, "PlatformDeploymentCompletedV1", { enumerable: true, get: function() {
|
|
629
|
+
return detail_types_1.PlatformDeploymentCompletedV1;
|
|
630
|
+
} });
|
|
631
|
+
Object.defineProperty(exports2, "PlatformSystemDataSeededV1", { enumerable: true, get: function() {
|
|
632
|
+
return detail_types_1.PlatformSystemDataSeededV1;
|
|
633
|
+
} });
|
|
634
|
+
Object.defineProperty(exports2, "defineDetailType", { enumerable: true, get: function() {
|
|
635
|
+
return detail_types_1.defineDetailType;
|
|
636
|
+
} });
|
|
637
|
+
Object.defineProperty(exports2, "isWellFormedDetailType", { enumerable: true, get: function() {
|
|
638
|
+
return detail_types_1.isWellFormedDetailType;
|
|
639
|
+
} });
|
|
640
|
+
var publisher_1 = require_publisher();
|
|
641
|
+
Object.defineProperty(exports2, "WorkflowPublishError", { enumerable: true, get: function() {
|
|
642
|
+
return publisher_1.WorkflowPublishError;
|
|
643
|
+
} });
|
|
644
|
+
Object.defineProperty(exports2, "publishWorkflowEvent", { enumerable: true, get: function() {
|
|
645
|
+
return publisher_1.publishWorkflowEvent;
|
|
646
|
+
} });
|
|
647
|
+
Object.defineProperty(exports2, "workflowsClient", { enumerable: true, get: function() {
|
|
648
|
+
return publisher_1.workflowsClient;
|
|
649
|
+
} });
|
|
650
|
+
var consumer_1 = require_consumer();
|
|
651
|
+
Object.defineProperty(exports2, "InvalidWorkflowEventError", { enumerable: true, get: function() {
|
|
652
|
+
return consumer_1.InvalidWorkflowEventError;
|
|
653
|
+
} });
|
|
654
|
+
Object.defineProperty(exports2, "UnsupportedEnvelopeVersionError", { enumerable: true, get: function() {
|
|
655
|
+
return consumer_1.UnsupportedEnvelopeVersionError;
|
|
656
|
+
} });
|
|
657
|
+
Object.defineProperty(exports2, "parseWorkflowEvent", { enumerable: true, get: function() {
|
|
658
|
+
return consumer_1.parseWorkflowEvent;
|
|
659
|
+
} });
|
|
660
|
+
var dedup_1 = require_dedup();
|
|
661
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS", { enumerable: true, get: function() {
|
|
662
|
+
return dedup_1.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;
|
|
663
|
+
} });
|
|
664
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH", { enumerable: true, get: function() {
|
|
665
|
+
return dedup_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH;
|
|
666
|
+
} });
|
|
667
|
+
Object.defineProperty(exports2, "WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR", { enumerable: true, get: function() {
|
|
668
|
+
return dedup_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR;
|
|
669
|
+
} });
|
|
670
|
+
Object.defineProperty(exports2, "WorkflowDedupInvalidInputError", { enumerable: true, get: function() {
|
|
671
|
+
return dedup_1.WorkflowDedupInvalidInputError;
|
|
672
|
+
} });
|
|
673
|
+
Object.defineProperty(exports2, "WorkflowDedupTableNameMissingError", { enumerable: true, get: function() {
|
|
674
|
+
return dedup_1.WorkflowDedupTableNameMissingError;
|
|
675
|
+
} });
|
|
676
|
+
Object.defineProperty(exports2, "encodeSortKey", { enumerable: true, get: function() {
|
|
677
|
+
return dedup_1.encodeSortKey;
|
|
678
|
+
} });
|
|
679
|
+
Object.defineProperty(exports2, "markFailed", { enumerable: true, get: function() {
|
|
680
|
+
return dedup_1.markFailed;
|
|
681
|
+
} });
|
|
682
|
+
Object.defineProperty(exports2, "recordIfAbsent", { enumerable: true, get: function() {
|
|
683
|
+
return dedup_1.recordIfAbsent;
|
|
684
|
+
} });
|
|
685
|
+
Object.defineProperty(exports2, "workflowDedupClient", { enumerable: true, get: function() {
|
|
686
|
+
return dedup_1.workflowDedupClient;
|
|
687
|
+
} });
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
|
|
93
691
|
// src/index.ts
|
|
94
692
|
var src_exports = {};
|
|
95
693
|
__export(src_exports, {
|
|
694
|
+
BRIDGED_STATUSES: () => BRIDGED_STATUSES,
|
|
695
|
+
CLOUDFORMATION_EVENT_SOURCE: () => CLOUDFORMATION_EVENT_SOURCE,
|
|
696
|
+
CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE: () => CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE,
|
|
697
|
+
CONTROL_EVENT_BUS_NAME_ENV_VAR: () => CONTROL_EVENT_BUS_NAME_ENV_VAR,
|
|
96
698
|
ChildHostedZone: () => ChildHostedZone,
|
|
97
699
|
CognitoFixtureSeederClient: () => CognitoFixtureSeederClient,
|
|
98
700
|
CognitoUserPool: () => CognitoUserPool,
|
|
@@ -103,11 +705,22 @@ __export(src_exports, {
|
|
|
103
705
|
DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES: () => DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES,
|
|
104
706
|
DATA_STORE_CHANGE_DETAIL_TYPE: () => DATA_STORE_CHANGE_DETAIL_TYPE,
|
|
105
707
|
DATA_STORE_CHANGE_EVENT_SOURCE: () => DATA_STORE_CHANGE_EVENT_SOURCE,
|
|
708
|
+
DEMO_PERIOD: () => DEMO_PERIOD,
|
|
709
|
+
DEMO_TENANT_SPECS: () => DEMO_TENANT_SPECS,
|
|
710
|
+
DEMO_URN_SYSTEM: () => DEMO_URN_SYSTEM,
|
|
711
|
+
DEV_USERS: () => DEV_USERS,
|
|
106
712
|
DataEventBus: () => DataEventBus,
|
|
107
713
|
DataStoreHistoricalArchive: () => DataStoreHistoricalArchive,
|
|
108
714
|
DataStorePostgresReplica: () => DataStorePostgresReplica,
|
|
109
715
|
DiscoverableStringParameter: () => DiscoverableStringParameter,
|
|
110
716
|
DynamoDbDataStore: () => DynamoDbDataStore,
|
|
717
|
+
OPENHI_REPO_TAG_KEY_ENV_VAR: () => OPENHI_REPO_TAG_KEY_ENV_VAR,
|
|
718
|
+
OPENHI_RESOURCE_URN_SYSTEM: () => OPENHI_RESOURCE_URN_SYSTEM,
|
|
719
|
+
OPENHI_TAG_KEY_PREFIX_ENV_VAR: () => OPENHI_TAG_KEY_PREFIX_ENV_VAR,
|
|
720
|
+
OPENHI_TAG_SUFFIX_BRANCH_NAME: () => OPENHI_TAG_SUFFIX_BRANCH_NAME,
|
|
721
|
+
OPENHI_TAG_SUFFIX_REPO_NAME: () => OPENHI_TAG_SUFFIX_REPO_NAME,
|
|
722
|
+
OPENHI_TAG_SUFFIX_SERVICE_TYPE: () => OPENHI_TAG_SUFFIX_SERVICE_TYPE,
|
|
723
|
+
OPENHI_TAG_SUFFIX_STAGE_TYPE: () => OPENHI_TAG_SUFFIX_STAGE_TYPE,
|
|
111
724
|
OpenHiApp: () => OpenHiApp,
|
|
112
725
|
OpenHiAuthService: () => OpenHiAuthService,
|
|
113
726
|
OpenHiDataService: () => OpenHiDataService,
|
|
@@ -118,10 +731,17 @@ __export(src_exports, {
|
|
|
118
731
|
OpenHiService: () => OpenHiService,
|
|
119
732
|
OpenHiStage: () => OpenHiStage,
|
|
120
733
|
OpsEventBus: () => OpsEventBus,
|
|
734
|
+
PLACEHOLDER_TENANT_ID: () => PLACEHOLDER_TENANT_ID,
|
|
735
|
+
PLACEHOLDER_WORKSPACE_ID: () => PLACEHOLDER_WORKSPACE_ID,
|
|
736
|
+
PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM: () => PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM,
|
|
737
|
+
PLATFORM_SCOPE_TENANT_ID: () => PLATFORM_SCOPE_TENANT_ID,
|
|
121
738
|
POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME: () => POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
|
|
122
739
|
POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME: () => POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
|
|
123
740
|
POSTGRES_REPLICA_SECRET_ARN_SSM_NAME: () => POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
|
|
124
741
|
PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE: () => PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE,
|
|
742
|
+
PlatformDeployBridge: () => PlatformDeployBridge,
|
|
743
|
+
PlatformDeployBridgeLambda: () => PlatformDeployBridgeLambda,
|
|
744
|
+
PlatformDeploymentCompletedV1: () => import_workflows4.PlatformDeploymentCompletedV1,
|
|
125
745
|
PostAuthenticationLambda: () => PostAuthenticationLambda,
|
|
126
746
|
PostConfirmationLambda: () => PostConfirmationLambda,
|
|
127
747
|
PreTokenGenerationLambda: () => PreTokenGenerationLambda,
|
|
@@ -131,14 +751,40 @@ __export(src_exports, {
|
|
|
131
751
|
RootHostedZone: () => RootHostedZone,
|
|
132
752
|
RootHttpApi: () => RootHttpApi,
|
|
133
753
|
RootWildcardCertificate: () => RootWildcardCertificate,
|
|
754
|
+
SEED_DEMO_DATA_CONSUMER_NAME: () => SEED_DEMO_DATA_CONSUMER_NAME,
|
|
755
|
+
SEED_SYSTEM_DATA_ACTOR_SYSTEM: () => SEED_SYSTEM_DATA_ACTOR_SYSTEM,
|
|
756
|
+
SEED_SYSTEM_DATA_CONSUMER_NAME: () => SEED_SYSTEM_DATA_CONSUMER_NAME,
|
|
757
|
+
SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR: () => SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR,
|
|
134
758
|
STATIC_HOSTING_SERVICE_TYPE: () => STATIC_HOSTING_SERVICE_TYPE,
|
|
759
|
+
SeedDemoDataLambda: () => SeedDemoDataLambda,
|
|
760
|
+
SeedDemoDataWorkflow: () => SeedDemoDataWorkflow,
|
|
761
|
+
SeedSystemDataLambda: () => SeedSystemDataLambda,
|
|
762
|
+
SeedSystemDataWorkflow: () => SeedSystemDataWorkflow,
|
|
135
763
|
StaticHosting: () => StaticHosting,
|
|
136
764
|
USER_ONBOARDING_EVENT_SOURCE: () => USER_ONBOARDING_EVENT_SOURCE,
|
|
137
765
|
UserOnboardingWorkflow: () => UserOnboardingWorkflow,
|
|
766
|
+
WorkflowDedupConsumerNameInvalidError: () => WorkflowDedupConsumerNameInvalidError,
|
|
767
|
+
WorkflowDedupTable: () => WorkflowDedupTable,
|
|
768
|
+
WorkflowDedupTableDuplicateError: () => WorkflowDedupTableDuplicateError,
|
|
138
769
|
buildFhirCurrentResourceChangeDetail: () => buildFhirCurrentResourceChangeDetail,
|
|
139
770
|
buildProvisionDefaultWorkspaceRequestedDetail: () => buildProvisionDefaultWorkspaceRequestedDetail,
|
|
771
|
+
demoBasePartitionKeys: () => demoBasePartitionKeys,
|
|
772
|
+
demoDevUserPartitionKeys: () => demoDevUserPartitionKeys,
|
|
773
|
+
demoMembershipId: () => demoMembershipId,
|
|
774
|
+
demoMembershipPartitionKey: () => demoMembershipPartitionKey,
|
|
775
|
+
demoRoleAssignmentId: () => demoRoleAssignmentId,
|
|
776
|
+
demoRoleAssignmentPartitionKey: () => demoRoleAssignmentPartitionKey,
|
|
777
|
+
demoRolesForUserInTenant: () => demoRolesForUserInTenant,
|
|
778
|
+
demoScenarioIdentifier: () => demoScenarioIdentifier,
|
|
779
|
+
demoTenantPartitionKey: () => demoTenantPartitionKey,
|
|
780
|
+
demoUserPartitionKey: () => demoUserPartitionKey,
|
|
781
|
+
demoWorkspacePartitionKey: () => demoWorkspacePartitionKey,
|
|
140
782
|
getDynamoDbDataStoreTableName: () => getDynamoDbDataStoreTableName,
|
|
141
|
-
getPostgresReplicaSchemaName: () => getPostgresReplicaSchemaName
|
|
783
|
+
getPostgresReplicaSchemaName: () => getPostgresReplicaSchemaName,
|
|
784
|
+
getWorkflowDedupTableName: () => getWorkflowDedupTableName,
|
|
785
|
+
openHiTagKey: () => openHiTagKey,
|
|
786
|
+
openhiResourceIdentifier: () => openhiResourceIdentifier,
|
|
787
|
+
rolePartitionKey: () => rolePartitionKey
|
|
142
788
|
});
|
|
143
789
|
module.exports = __toCommonJS(src_exports);
|
|
144
790
|
|
|
@@ -353,6 +999,11 @@ var import_utils = require("@codedrifters/utils");
|
|
|
353
999
|
var import_config3 = __toESM(require_lib());
|
|
354
1000
|
var import_aws_cdk_lib4 = require("aws-cdk-lib");
|
|
355
1001
|
var import_change_case = require("change-case");
|
|
1002
|
+
var OPENHI_TAG_SUFFIX_REPO_NAME = "repo-name";
|
|
1003
|
+
var OPENHI_TAG_SUFFIX_BRANCH_NAME = "branch-name";
|
|
1004
|
+
var OPENHI_TAG_SUFFIX_SERVICE_TYPE = "service-type";
|
|
1005
|
+
var OPENHI_TAG_SUFFIX_STAGE_TYPE = "stage-type";
|
|
1006
|
+
var openHiTagKey = (appName, suffix) => `${appName}:${suffix}`;
|
|
356
1007
|
var OpenHiService = class extends import_aws_cdk_lib4.Stack {
|
|
357
1008
|
/**
|
|
358
1009
|
* Creates a new OpenHI service stack.
|
|
@@ -420,11 +1071,20 @@ var OpenHiService = class extends import_aws_cdk_lib4.Stack {
|
|
|
420
1071
|
`availability-zones:account=${account}:region=${region}`,
|
|
421
1072
|
[`${region}a`, `${region}b`, `${region}c`]
|
|
422
1073
|
);
|
|
423
|
-
import_aws_cdk_lib4.Tags.of(this).add(`${appName}:repo-name`, repoName.slice(0, 255));
|
|
424
|
-
import_aws_cdk_lib4.Tags.of(this).add(`${appName}:branch-name`, branchName.slice(0, 255));
|
|
425
|
-
import_aws_cdk_lib4.Tags.of(this).add(`${appName}:service-type`, id.slice(0, 255));
|
|
426
1074
|
import_aws_cdk_lib4.Tags.of(this).add(
|
|
427
|
-
|
|
1075
|
+
openHiTagKey(appName, OPENHI_TAG_SUFFIX_REPO_NAME),
|
|
1076
|
+
repoName.slice(0, 255)
|
|
1077
|
+
);
|
|
1078
|
+
import_aws_cdk_lib4.Tags.of(this).add(
|
|
1079
|
+
openHiTagKey(appName, OPENHI_TAG_SUFFIX_BRANCH_NAME),
|
|
1080
|
+
branchName.slice(0, 255)
|
|
1081
|
+
);
|
|
1082
|
+
import_aws_cdk_lib4.Tags.of(this).add(
|
|
1083
|
+
openHiTagKey(appName, OPENHI_TAG_SUFFIX_SERVICE_TYPE),
|
|
1084
|
+
id.slice(0, 255)
|
|
1085
|
+
);
|
|
1086
|
+
import_aws_cdk_lib4.Tags.of(this).add(
|
|
1087
|
+
openHiTagKey(appName, OPENHI_TAG_SUFFIX_STAGE_TYPE),
|
|
428
1088
|
ohEnv.ohStage.stageType.slice(0, 255)
|
|
429
1089
|
);
|
|
430
1090
|
}
|
|
@@ -1110,6 +1770,204 @@ var DynamoDbDataStore = class extends import_aws_dynamodb.Table {
|
|
|
1110
1770
|
}
|
|
1111
1771
|
};
|
|
1112
1772
|
|
|
1773
|
+
// src/components/dynamodb/workflow-dedup-table.ts
|
|
1774
|
+
var import_workflows = __toESM(require_lib2());
|
|
1775
|
+
var import_aws_cdk_lib8 = require("aws-cdk-lib");
|
|
1776
|
+
var import_aws_dynamodb2 = require("aws-cdk-lib/aws-dynamodb");
|
|
1777
|
+
var import_aws_iam = require("aws-cdk-lib/aws-iam");
|
|
1778
|
+
var import_constructs5 = require("constructs");
|
|
1779
|
+
function getWorkflowDedupTableName(scope) {
|
|
1780
|
+
const stack = OpenHiService.of(scope);
|
|
1781
|
+
return `workflow-dedup-${stack.branchHash}`;
|
|
1782
|
+
}
|
|
1783
|
+
var _WorkflowDedupTable = class _WorkflowDedupTable extends import_constructs5.Construct {
|
|
1784
|
+
constructor(scope, id, props = {}) {
|
|
1785
|
+
super(scope, id);
|
|
1786
|
+
this.registeredConsumers = /* @__PURE__ */ new Set();
|
|
1787
|
+
const service = OpenHiService.of(scope);
|
|
1788
|
+
const others = service.node.findAll().filter(
|
|
1789
|
+
(c) => c instanceof _WorkflowDedupTable && c !== this
|
|
1790
|
+
);
|
|
1791
|
+
if (others.length > 0) {
|
|
1792
|
+
throw new WorkflowDedupTableDuplicateError(
|
|
1793
|
+
`WorkflowDedupTable already exists at ${others[0].node.path}; only one shared dedup table is allowed per service stack (TR-015).`
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
this.table = new import_aws_dynamodb2.Table(this, "Table", {
|
|
1797
|
+
tableName: getWorkflowDedupTableName(scope),
|
|
1798
|
+
partitionKey: {
|
|
1799
|
+
name: "consumerName",
|
|
1800
|
+
type: import_aws_dynamodb2.AttributeType.STRING
|
|
1801
|
+
},
|
|
1802
|
+
sortKey: {
|
|
1803
|
+
name: "sk",
|
|
1804
|
+
type: import_aws_dynamodb2.AttributeType.STRING
|
|
1805
|
+
},
|
|
1806
|
+
billingMode: import_aws_dynamodb2.BillingMode.PAY_PER_REQUEST,
|
|
1807
|
+
timeToLiveAttribute: "expiresAt",
|
|
1808
|
+
removalPolicy: props.removalPolicy ?? service.removalPolicy
|
|
1809
|
+
});
|
|
1810
|
+
new DiscoverableStringParameter(this, "table-name-param", {
|
|
1811
|
+
ssmParamName: _WorkflowDedupTable.TABLE_NAME_SSM_PARAM_NAME,
|
|
1812
|
+
stringValue: this.table.tableName
|
|
1813
|
+
});
|
|
1814
|
+
new DiscoverableStringParameter(this, "table-arn-param", {
|
|
1815
|
+
ssmParamName: _WorkflowDedupTable.TABLE_ARN_SSM_PARAM_NAME,
|
|
1816
|
+
stringValue: this.table.tableArn
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
/** Cross-stack lookup for the table name. */
|
|
1820
|
+
static tableNameFromLookup(scope) {
|
|
1821
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
1822
|
+
ssmParamName: _WorkflowDedupTable.TABLE_NAME_SSM_PARAM_NAME,
|
|
1823
|
+
serviceType: _WorkflowDedupTable.PUBLISHER_SERVICE_TYPE
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
/** Cross-stack lookup for the table ARN. */
|
|
1827
|
+
static tableArnFromLookup(scope) {
|
|
1828
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
1829
|
+
ssmParamName: _WorkflowDedupTable.TABLE_ARN_SSM_PARAM_NAME,
|
|
1830
|
+
serviceType: _WorkflowDedupTable.PUBLISHER_SERVICE_TYPE
|
|
1831
|
+
});
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Cross-stack equivalent of {@link grantConsumer}. Use when the dedup
|
|
1835
|
+
* table is on a different stack than the consumer Lambda — the
|
|
1836
|
+
* grant resolves the table name + ARN via SSM at synth time, so the
|
|
1837
|
+
* consumer stack does not pick up a CloudFormation export dependency
|
|
1838
|
+
* on the global stack.
|
|
1839
|
+
*
|
|
1840
|
+
* Inverts the singleton-guard semantics of `grantConsumer`: there is
|
|
1841
|
+
* no synth-time check that the same `consumerName` was registered
|
|
1842
|
+
* twice across stacks. Consumer names are agreed by convention
|
|
1843
|
+
* (see TR-015); double-registration is operator error caught at
|
|
1844
|
+
* design time, not synth time.
|
|
1845
|
+
*/
|
|
1846
|
+
static grantConsumerFromLookup(scope, fn, consumerName, options = {}) {
|
|
1847
|
+
_WorkflowDedupTable.assertConsumerNameStatic(consumerName);
|
|
1848
|
+
const tableName = _WorkflowDedupTable.tableNameFromLookup(scope);
|
|
1849
|
+
const tableArn = _WorkflowDedupTable.tableArnFromLookup(scope);
|
|
1850
|
+
fn.addEnvironment(import_workflows.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR, tableName);
|
|
1851
|
+
if (options.defaultTtlSeconds !== void 0) {
|
|
1852
|
+
fn.addEnvironment(
|
|
1853
|
+
"OPENHI_WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS",
|
|
1854
|
+
String(options.defaultTtlSeconds)
|
|
1855
|
+
);
|
|
1856
|
+
}
|
|
1857
|
+
fn.addToRolePolicy(
|
|
1858
|
+
new import_aws_iam.PolicyStatement({
|
|
1859
|
+
effect: import_aws_iam.Effect.ALLOW,
|
|
1860
|
+
actions: [
|
|
1861
|
+
"dynamodb:PutItem",
|
|
1862
|
+
"dynamodb:UpdateItem",
|
|
1863
|
+
"dynamodb:GetItem",
|
|
1864
|
+
"dynamodb:Query"
|
|
1865
|
+
],
|
|
1866
|
+
resources: [tableArn],
|
|
1867
|
+
conditions: {
|
|
1868
|
+
"ForAllValues:StringEquals": {
|
|
1869
|
+
"dynamodb:LeadingKeys": [consumerName]
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
})
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* Standalone consumer-name validator shared by the instance method
|
|
1877
|
+
* and `grantConsumerFromLookup` so the two grants enforce identical
|
|
1878
|
+
* invariants.
|
|
1879
|
+
*/
|
|
1880
|
+
static assertConsumerNameStatic(consumerName) {
|
|
1881
|
+
if (consumerName.length === 0) {
|
|
1882
|
+
throw new WorkflowDedupConsumerNameInvalidError(
|
|
1883
|
+
"consumerName must be non-empty."
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
if (consumerName.length > import_workflows.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH) {
|
|
1887
|
+
throw new WorkflowDedupConsumerNameInvalidError(
|
|
1888
|
+
`consumerName must be at most ${import_workflows.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH} chars; got ${consumerName.length}.`
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
if (/\s/.test(consumerName)) {
|
|
1892
|
+
throw new WorkflowDedupConsumerNameInvalidError(
|
|
1893
|
+
"consumerName must not contain whitespace."
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Wire a Lambda consumer to this table. Injects the table-name env var
|
|
1899
|
+
* so the runtime `WorkflowDedupClient` can resolve it, then attaches a
|
|
1900
|
+
* per-consumer IAM grant scoped by `dynamodb:LeadingKeys` so the
|
|
1901
|
+
* consumer can only read/write its own partition.
|
|
1902
|
+
*/
|
|
1903
|
+
grantConsumer(fn, consumerName, options = {}) {
|
|
1904
|
+
this.assertConsumerName(consumerName);
|
|
1905
|
+
if (this.registeredConsumers.has(consumerName)) {
|
|
1906
|
+
import_aws_cdk_lib8.Annotations.of(this).addWarning(
|
|
1907
|
+
`WorkflowDedupTable: consumerName "${consumerName}" registered more than once; subsequent grantConsumer calls add policy statements but do not re-inject the env var.`
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
this.registeredConsumers.add(consumerName);
|
|
1911
|
+
fn.addEnvironment(import_workflows.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR, this.table.tableName);
|
|
1912
|
+
if (options.defaultTtlSeconds !== void 0) {
|
|
1913
|
+
fn.addEnvironment(
|
|
1914
|
+
"OPENHI_WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS",
|
|
1915
|
+
String(options.defaultTtlSeconds)
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1918
|
+
fn.addToRolePolicy(
|
|
1919
|
+
new import_aws_iam.PolicyStatement({
|
|
1920
|
+
effect: import_aws_iam.Effect.ALLOW,
|
|
1921
|
+
actions: [
|
|
1922
|
+
"dynamodb:PutItem",
|
|
1923
|
+
"dynamodb:UpdateItem",
|
|
1924
|
+
"dynamodb:GetItem",
|
|
1925
|
+
"dynamodb:Query"
|
|
1926
|
+
],
|
|
1927
|
+
resources: [this.table.tableArn],
|
|
1928
|
+
conditions: {
|
|
1929
|
+
"ForAllValues:StringEquals": {
|
|
1930
|
+
"dynamodb:LeadingKeys": [consumerName]
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
})
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
assertConsumerName(consumerName) {
|
|
1937
|
+
_WorkflowDedupTable.assertConsumerNameStatic(consumerName);
|
|
1938
|
+
}
|
|
1939
|
+
};
|
|
1940
|
+
/** SSM param name (short) used by `DiscoverableStringParameter` for the table name lookup. */
|
|
1941
|
+
_WorkflowDedupTable.TABLE_NAME_SSM_PARAM_NAME = "workflow-dedup-table-name";
|
|
1942
|
+
/** SSM param name (short) used by `DiscoverableStringParameter` for the table ARN lookup. */
|
|
1943
|
+
_WorkflowDedupTable.TABLE_ARN_SSM_PARAM_NAME = "workflow-dedup-table-arn";
|
|
1944
|
+
/**
|
|
1945
|
+
* Service-type the publishing stack runs under. The cross-stack lookups
|
|
1946
|
+
* pin to this value so consumer stacks on a different service-type
|
|
1947
|
+
* (e.g. `data`, `auth`) resolve the parameter at the publisher's SSM
|
|
1948
|
+
* path instead of their own. Typed against `OpenHiServiceType` so a
|
|
1949
|
+
* future rename of the literal triggers a compile error; not pulled
|
|
1950
|
+
* from `OpenHiGlobalService.SERVICE_TYPE` because
|
|
1951
|
+
* `OpenHiGlobalService` already imports `WorkflowDedupTable` — a
|
|
1952
|
+
* back-import would create a circular dependency.
|
|
1953
|
+
*/
|
|
1954
|
+
_WorkflowDedupTable.PUBLISHER_SERVICE_TYPE = "global";
|
|
1955
|
+
var WorkflowDedupTable = _WorkflowDedupTable;
|
|
1956
|
+
var WorkflowDedupTableDuplicateError = class extends Error {
|
|
1957
|
+
/** @param message - human-readable description of the duplicate. */
|
|
1958
|
+
constructor(message) {
|
|
1959
|
+
super(message);
|
|
1960
|
+
this.name = "WorkflowDedupTableDuplicateError";
|
|
1961
|
+
}
|
|
1962
|
+
};
|
|
1963
|
+
var WorkflowDedupConsumerNameInvalidError = class extends Error {
|
|
1964
|
+
/** @param message - human-readable description of the invariant violation. */
|
|
1965
|
+
constructor(message) {
|
|
1966
|
+
super(message);
|
|
1967
|
+
this.name = "WorkflowDedupConsumerNameInvalidError";
|
|
1968
|
+
}
|
|
1969
|
+
};
|
|
1970
|
+
|
|
1113
1971
|
// src/components/event-bridge/data-event-bus.ts
|
|
1114
1972
|
var import_aws_events = require("aws-cdk-lib/aws-events");
|
|
1115
1973
|
var DataEventBus = class _DataEventBus extends import_aws_events.EventBus {
|
|
@@ -1179,13 +2037,13 @@ var ControlEventBus = class _ControlEventBus extends import_aws_events3.EventBus
|
|
|
1179
2037
|
// src/components/postgres/data-store-postgres-replica.ts
|
|
1180
2038
|
var import_node_fs5 = __toESM(require("fs"));
|
|
1181
2039
|
var import_node_path5 = __toESM(require("path"));
|
|
1182
|
-
var
|
|
2040
|
+
var import_aws_cdk_lib9 = require("aws-cdk-lib");
|
|
1183
2041
|
var ec2 = __toESM(require("aws-cdk-lib/aws-ec2"));
|
|
1184
2042
|
var import_aws_lambda5 = require("aws-cdk-lib/aws-lambda");
|
|
1185
2043
|
var import_aws_lambda_event_sources = require("aws-cdk-lib/aws-lambda-event-sources");
|
|
1186
2044
|
var import_aws_lambda_nodejs5 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
1187
2045
|
var rds = __toESM(require("aws-cdk-lib/aws-rds"));
|
|
1188
|
-
var
|
|
2046
|
+
var import_constructs6 = require("constructs");
|
|
1189
2047
|
var HANDLER_NAME5 = "data-store-postgres-replication.handler.js";
|
|
1190
2048
|
var DEFAULT_DATABASE_NAME = "openhi";
|
|
1191
2049
|
var SCHEMA_NAME_PATTERN = /^[a-z_][a-z0-9_]{0,62}$/;
|
|
@@ -1208,7 +2066,7 @@ function getPostgresReplicaSchemaName(branchHash) {
|
|
|
1208
2066
|
}
|
|
1209
2067
|
return candidate;
|
|
1210
2068
|
}
|
|
1211
|
-
var DataStorePostgresReplica = class extends
|
|
2069
|
+
var DataStorePostgresReplica = class extends import_constructs6.Construct {
|
|
1212
2070
|
/**
|
|
1213
2071
|
* Resolve the cluster ARN published by an upstream {@link DataStorePostgresReplica}.
|
|
1214
2072
|
* Use from any stack that needs to grant `rds-data:ExecuteStatement` against
|
|
@@ -1245,7 +2103,7 @@ var DataStorePostgresReplica = class extends import_constructs5.Construct {
|
|
|
1245
2103
|
super(scope, id);
|
|
1246
2104
|
this.databaseName = props.databaseName ?? DEFAULT_DATABASE_NAME;
|
|
1247
2105
|
this.schemaName = getPostgresReplicaSchemaName(props.branchHash);
|
|
1248
|
-
const region =
|
|
2106
|
+
const region = import_aws_cdk_lib9.Stack.of(this).region;
|
|
1249
2107
|
this.vpc = props.vpc ?? new ec2.Vpc(this, "Vpc", {
|
|
1250
2108
|
availabilityZones: [`${region}a`, `${region}b`],
|
|
1251
2109
|
natGateways: 0,
|
|
@@ -1281,7 +2139,7 @@ var DataStorePostgresReplica = class extends import_constructs5.Construct {
|
|
|
1281
2139
|
entry: resolveHandlerEntry5(__dirname),
|
|
1282
2140
|
runtime: import_aws_lambda5.Runtime.NODEJS_LATEST,
|
|
1283
2141
|
memorySize: 512,
|
|
1284
|
-
timeout:
|
|
2142
|
+
timeout: import_aws_cdk_lib9.Duration.minutes(1),
|
|
1285
2143
|
vpc: this.vpc,
|
|
1286
2144
|
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
|
|
1287
2145
|
description: "Replicates DynamoDB current-resource changes into the Postgres `resources` JSONB table (ADR 2026-04-17-01).",
|
|
@@ -1308,7 +2166,7 @@ var DataStorePostgresReplica = class extends import_constructs5.Construct {
|
|
|
1308
2166
|
new import_aws_lambda_event_sources.KinesisEventSource(props.kinesisStream, {
|
|
1309
2167
|
startingPosition: import_aws_lambda5.StartingPosition.LATEST,
|
|
1310
2168
|
batchSize: 100,
|
|
1311
|
-
maxBatchingWindow:
|
|
2169
|
+
maxBatchingWindow: import_aws_cdk_lib9.Duration.seconds(5),
|
|
1312
2170
|
retryAttempts: 10,
|
|
1313
2171
|
bisectBatchOnError: true,
|
|
1314
2172
|
parallelizationFactor: 2,
|
|
@@ -1341,7 +2199,7 @@ var DataStorePostgresReplica = class extends import_constructs5.Construct {
|
|
|
1341
2199
|
};
|
|
1342
2200
|
|
|
1343
2201
|
// src/components/route-53/child-hosted-zone.ts
|
|
1344
|
-
var
|
|
2202
|
+
var import_aws_cdk_lib10 = require("aws-cdk-lib");
|
|
1345
2203
|
var import_aws_route53 = require("aws-cdk-lib/aws-route53");
|
|
1346
2204
|
var ChildHostedZone = class extends import_aws_route53.HostedZone {
|
|
1347
2205
|
constructor(scope, id, props) {
|
|
@@ -1350,7 +2208,7 @@ var ChildHostedZone = class extends import_aws_route53.HostedZone {
|
|
|
1350
2208
|
zone: props.parentHostedZone,
|
|
1351
2209
|
recordName: this.zoneName,
|
|
1352
2210
|
values: this.hostedZoneNameServers || [],
|
|
1353
|
-
ttl:
|
|
2211
|
+
ttl: import_aws_cdk_lib10.Duration.minutes(5)
|
|
1354
2212
|
});
|
|
1355
2213
|
}
|
|
1356
2214
|
};
|
|
@@ -1360,8 +2218,8 @@ var ChildHostedZone = class extends import_aws_route53.HostedZone {
|
|
|
1360
2218
|
ChildHostedZone.SSM_PARAM_NAME = "CHILDHOSTEDZONE";
|
|
1361
2219
|
|
|
1362
2220
|
// src/components/route-53/root-hosted-zone.ts
|
|
1363
|
-
var
|
|
1364
|
-
var RootHostedZone = class extends
|
|
2221
|
+
var import_constructs7 = require("constructs");
|
|
2222
|
+
var RootHostedZone = class extends import_constructs7.Construct {
|
|
1365
2223
|
};
|
|
1366
2224
|
|
|
1367
2225
|
// src/components/static-hosting/static-hosting.ts
|
|
@@ -1369,9 +2227,9 @@ var import_aws_cloudfront = require("aws-cdk-lib/aws-cloudfront");
|
|
|
1369
2227
|
var import_aws_cloudfront_origins = require("aws-cdk-lib/aws-cloudfront-origins");
|
|
1370
2228
|
var import_aws_s3 = require("aws-cdk-lib/aws-s3");
|
|
1371
2229
|
var import_core = require("aws-cdk-lib/core");
|
|
1372
|
-
var
|
|
2230
|
+
var import_constructs8 = require("constructs");
|
|
1373
2231
|
var STATIC_HOSTING_SERVICE_TYPE = "website";
|
|
1374
|
-
var _StaticHosting = class _StaticHosting extends
|
|
2232
|
+
var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
|
|
1375
2233
|
constructor(scope, id, props = {}) {
|
|
1376
2234
|
super(scope, id);
|
|
1377
2235
|
const stack = OpenHiService.of(scope);
|
|
@@ -1423,29 +2281,127 @@ _StaticHosting.SSM_PARAM_NAME_DISTRIBUTION_ARN = "STATIC_HOSTING_DISTRIBUTION_AR
|
|
|
1423
2281
|
var StaticHosting = _StaticHosting;
|
|
1424
2282
|
|
|
1425
2283
|
// src/services/open-hi-auth-service.ts
|
|
1426
|
-
var
|
|
2284
|
+
var import_config5 = __toESM(require_lib());
|
|
1427
2285
|
var import_aws_cognito5 = require("aws-cdk-lib/aws-cognito");
|
|
1428
|
-
var
|
|
2286
|
+
var import_aws_iam6 = require("aws-cdk-lib/aws-iam");
|
|
1429
2287
|
var import_aws_kms2 = require("aws-cdk-lib/aws-kms");
|
|
1430
2288
|
var import_core2 = require("aws-cdk-lib/core");
|
|
1431
2289
|
|
|
1432
2290
|
// src/services/open-hi-data-service.ts
|
|
1433
|
-
var
|
|
2291
|
+
var import_config4 = __toESM(require_lib());
|
|
2292
|
+
var import_aws_dynamodb3 = require("aws-cdk-lib/aws-dynamodb");
|
|
1434
2293
|
var kinesis = __toESM(require("aws-cdk-lib/aws-kinesis"));
|
|
1435
2294
|
|
|
1436
2295
|
// src/services/open-hi-global-service.ts
|
|
1437
2296
|
var import_aws_certificatemanager2 = require("aws-cdk-lib/aws-certificatemanager");
|
|
1438
|
-
var
|
|
2297
|
+
var import_aws_events5 = require("aws-cdk-lib/aws-events");
|
|
1439
2298
|
var import_aws_route532 = require("aws-cdk-lib/aws-route53");
|
|
1440
2299
|
var import_aws_ssm3 = require("aws-cdk-lib/aws-ssm");
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
2300
|
+
|
|
2301
|
+
// src/workflows/control-plane/platform-deploy-bridge/events.ts
|
|
2302
|
+
var CLOUDFORMATION_EVENT_SOURCE = "aws.cloudformation";
|
|
2303
|
+
var CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE = "CloudFormation Stack Status Change";
|
|
2304
|
+
var BRIDGED_STATUSES = ["CREATE_COMPLETE", "UPDATE_COMPLETE"];
|
|
2305
|
+
var CONTROL_EVENT_BUS_NAME_ENV_VAR = "CONTROL_EVENT_BUS_NAME";
|
|
2306
|
+
var OPENHI_REPO_TAG_KEY_ENV_VAR = "OPENHI_REPO_TAG_KEY";
|
|
2307
|
+
var OPENHI_TAG_KEY_PREFIX_ENV_VAR = "OPENHI_TAG_KEY_PREFIX";
|
|
2308
|
+
var PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM = "platform-deploy-bridge";
|
|
2309
|
+
|
|
2310
|
+
// src/workflows/control-plane/platform-deploy-bridge/platform-deploy-bridge.ts
|
|
2311
|
+
var import_constructs10 = require("constructs");
|
|
2312
|
+
|
|
2313
|
+
// src/workflows/control-plane/platform-deploy-bridge/platform-deploy-bridge-lambda.ts
|
|
2314
|
+
var import_node_fs6 = __toESM(require("fs"));
|
|
2315
|
+
var import_node_path6 = __toESM(require("path"));
|
|
2316
|
+
var import_aws_cdk_lib11 = require("aws-cdk-lib");
|
|
2317
|
+
var import_aws_events4 = require("aws-cdk-lib/aws-events");
|
|
2318
|
+
var import_aws_events_targets = require("aws-cdk-lib/aws-events-targets");
|
|
2319
|
+
var import_aws_iam2 = require("aws-cdk-lib/aws-iam");
|
|
2320
|
+
var import_aws_lambda6 = require("aws-cdk-lib/aws-lambda");
|
|
2321
|
+
var import_aws_lambda_nodejs6 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
2322
|
+
var import_constructs9 = require("constructs");
|
|
2323
|
+
var HANDLER_NAME6 = "platform-deploy-bridge.handler.js";
|
|
2324
|
+
function resolveHandlerEntry6(dirname) {
|
|
2325
|
+
const sameDir = import_node_path6.default.join(dirname, HANDLER_NAME6);
|
|
2326
|
+
if (import_node_fs6.default.existsSync(sameDir)) {
|
|
2327
|
+
return sameDir;
|
|
2328
|
+
}
|
|
2329
|
+
return import_node_path6.default.join(dirname, "..", "..", "..", "..", "lib", HANDLER_NAME6);
|
|
2330
|
+
}
|
|
2331
|
+
var PlatformDeployBridgeLambda = class extends import_constructs9.Construct {
|
|
2332
|
+
constructor(scope, props) {
|
|
2333
|
+
super(scope, "platform-deploy-bridge-lambda");
|
|
2334
|
+
const service = OpenHiService.of(this);
|
|
2335
|
+
const repoTagKey = openHiTagKey(
|
|
2336
|
+
service.appName,
|
|
2337
|
+
OPENHI_TAG_SUFFIX_REPO_NAME
|
|
2338
|
+
);
|
|
2339
|
+
const tagKeyPrefix = `${service.appName}:`;
|
|
2340
|
+
const ownStackName = import_aws_cdk_lib11.Stack.of(this).stackName;
|
|
2341
|
+
const ownSuffix = `-${service.serviceId}-${import_aws_cdk_lib11.Stack.of(this).account}-${import_aws_cdk_lib11.Stack.of(this).region}`;
|
|
2342
|
+
const sharedPrefix = ownStackName.endsWith(ownSuffix) ? ownStackName.slice(0, -ownSuffix.length) : service.branchHash;
|
|
2343
|
+
const stackIdPrefix = `arn:aws:cloudformation:${import_aws_cdk_lib11.Stack.of(this).region}:${import_aws_cdk_lib11.Stack.of(this).account}:stack/${sharedPrefix}-`;
|
|
2344
|
+
this.lambda = new import_aws_lambda_nodejs6.NodejsFunction(this, "handler", {
|
|
2345
|
+
entry: resolveHandlerEntry6(__dirname),
|
|
2346
|
+
runtime: import_aws_lambda6.Runtime.NODEJS_LATEST,
|
|
2347
|
+
memorySize: 256,
|
|
2348
|
+
timeout: import_aws_cdk_lib11.Duration.seconds(30),
|
|
2349
|
+
environment: {
|
|
2350
|
+
[CONTROL_EVENT_BUS_NAME_ENV_VAR]: props.controlEventBus.eventBusName,
|
|
2351
|
+
[OPENHI_REPO_TAG_KEY_ENV_VAR]: repoTagKey,
|
|
2352
|
+
[OPENHI_TAG_KEY_PREFIX_ENV_VAR]: tagKeyPrefix
|
|
2353
|
+
}
|
|
2354
|
+
});
|
|
2355
|
+
this.lambda.addToRolePolicy(
|
|
2356
|
+
new import_aws_iam2.PolicyStatement({
|
|
2357
|
+
effect: import_aws_iam2.Effect.ALLOW,
|
|
2358
|
+
actions: ["cloudformation:DescribeStacks"],
|
|
2359
|
+
resources: [
|
|
2360
|
+
`arn:aws:cloudformation:${import_aws_cdk_lib11.Stack.of(this).region}:${import_aws_cdk_lib11.Stack.of(this).account}:stack/*`
|
|
2361
|
+
]
|
|
2362
|
+
})
|
|
2363
|
+
);
|
|
2364
|
+
props.controlEventBus.grantPutEventsTo(this.lambda);
|
|
2365
|
+
this.rule = new import_aws_events4.Rule(this, "rule", {
|
|
2366
|
+
eventPattern: {
|
|
2367
|
+
source: [CLOUDFORMATION_EVENT_SOURCE],
|
|
2368
|
+
detailType: [CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE],
|
|
2369
|
+
detail: {
|
|
2370
|
+
"stack-id": [{ prefix: stackIdPrefix }],
|
|
2371
|
+
"status-details": {
|
|
2372
|
+
status: [...BRIDGED_STATUSES]
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
},
|
|
2376
|
+
targets: [
|
|
2377
|
+
new import_aws_events_targets.LambdaFunction(this.lambda, {
|
|
2378
|
+
retryAttempts: 2,
|
|
2379
|
+
maxEventAge: import_aws_cdk_lib11.Duration.hours(2)
|
|
2380
|
+
})
|
|
2381
|
+
]
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2384
|
+
};
|
|
2385
|
+
|
|
2386
|
+
// src/workflows/control-plane/platform-deploy-bridge/platform-deploy-bridge.ts
|
|
2387
|
+
var PlatformDeployBridge = class extends import_constructs10.Construct {
|
|
2388
|
+
constructor(scope, props) {
|
|
2389
|
+
super(scope, "platform-deploy-bridge");
|
|
2390
|
+
this.bridgeLambda = new PlatformDeployBridgeLambda(this, {
|
|
2391
|
+
controlEventBus: props.controlEventBus
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
|
|
2396
|
+
// src/services/open-hi-global-service.ts
|
|
2397
|
+
var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
2398
|
+
/**
|
|
2399
|
+
* Returns an IHostedZone from the given attributes (no SSM). Use when the zone is imported from config.
|
|
2400
|
+
*/
|
|
2401
|
+
static rootHostedZoneFromConstruct(scope, props) {
|
|
2402
|
+
return import_aws_route532.HostedZone.fromHostedZoneAttributes(scope, "root-zone", props);
|
|
2403
|
+
}
|
|
2404
|
+
/**
|
|
1449
2405
|
* Returns an ICertificate by looking up the Global stack's wildcard cert ARN from SSM.
|
|
1450
2406
|
*/
|
|
1451
2407
|
static rootWildcardCertificateFromConstruct(scope) {
|
|
@@ -1476,7 +2432,7 @@ var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
|
1476
2432
|
* Returns the data event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.
|
|
1477
2433
|
*/
|
|
1478
2434
|
static dataEventBusFromConstruct(scope) {
|
|
1479
|
-
return
|
|
2435
|
+
return import_aws_events5.EventBus.fromEventBusName(
|
|
1480
2436
|
scope,
|
|
1481
2437
|
"data-event-bus",
|
|
1482
2438
|
DataEventBus.getEventBusName(scope)
|
|
@@ -1486,7 +2442,7 @@ var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
|
1486
2442
|
* Returns the ops event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.
|
|
1487
2443
|
*/
|
|
1488
2444
|
static opsEventBusFromConstruct(scope) {
|
|
1489
|
-
return
|
|
2445
|
+
return import_aws_events5.EventBus.fromEventBusName(
|
|
1490
2446
|
scope,
|
|
1491
2447
|
"ops-event-bus",
|
|
1492
2448
|
OpsEventBus.getEventBusName(scope)
|
|
@@ -1496,12 +2452,23 @@ var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
|
1496
2452
|
* Returns the control-plane event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.
|
|
1497
2453
|
*/
|
|
1498
2454
|
static controlEventBusFromConstruct(scope) {
|
|
1499
|
-
return
|
|
2455
|
+
return import_aws_events5.EventBus.fromEventBusName(
|
|
1500
2456
|
scope,
|
|
1501
2457
|
"control-event-bus",
|
|
1502
2458
|
ControlEventBus.getEventBusName(scope)
|
|
1503
2459
|
);
|
|
1504
2460
|
}
|
|
2461
|
+
/**
|
|
2462
|
+
* Returns the workflow dedup table by name (deterministic per branch).
|
|
2463
|
+
* Use from other stacks to obtain an ITable reference. Consumer Lambdas
|
|
2464
|
+
* are typically wired via `WorkflowDedupTable.grantConsumer(fn, name)`
|
|
2465
|
+
* on the owning service's `workflowDedupTable` reference; the
|
|
2466
|
+
* `tableNameFromLookup` / `tableArnFromLookup` SSM helpers on the
|
|
2467
|
+
* construct cover cross-stack consumers that need only the name/ARN.
|
|
2468
|
+
*/
|
|
2469
|
+
static workflowDedupTableNameFromLookup(scope) {
|
|
2470
|
+
return WorkflowDedupTable.tableNameFromLookup(scope);
|
|
2471
|
+
}
|
|
1505
2472
|
get serviceType() {
|
|
1506
2473
|
return _OpenHiGlobalService.SERVICE_TYPE;
|
|
1507
2474
|
}
|
|
@@ -1515,6 +2482,8 @@ var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
|
1515
2482
|
this.dataEventBus = this.createDataEventBus();
|
|
1516
2483
|
this.opsEventBus = this.createOpsEventBus();
|
|
1517
2484
|
this.controlEventBus = this.createControlEventBus();
|
|
2485
|
+
this.workflowDedupTable = this.createWorkflowDedupTable();
|
|
2486
|
+
this.platformDeployBridge = this.createPlatformDeployBridge();
|
|
1518
2487
|
}
|
|
1519
2488
|
/**
|
|
1520
2489
|
* Validates that config required for the Global stack is present.
|
|
@@ -1586,23 +2555,1246 @@ var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
|
1586
2555
|
createControlEventBus() {
|
|
1587
2556
|
return new ControlEventBus(this);
|
|
1588
2557
|
}
|
|
2558
|
+
/**
|
|
2559
|
+
* Creates the platform deploy bridge that republishes CloudFormation
|
|
2560
|
+
* Stack Status Change events onto the control event bus.
|
|
2561
|
+
* Override to customize.
|
|
2562
|
+
*/
|
|
2563
|
+
createPlatformDeployBridge() {
|
|
2564
|
+
return new PlatformDeployBridge(this, {
|
|
2565
|
+
controlEventBus: this.controlEventBus
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
/**
|
|
2569
|
+
* Creates the shared workflow dedup table (TR-015 singleton).
|
|
2570
|
+
* Override to customize.
|
|
2571
|
+
*/
|
|
2572
|
+
createWorkflowDedupTable() {
|
|
2573
|
+
return new WorkflowDedupTable(this, "workflow-dedup-table");
|
|
2574
|
+
}
|
|
1589
2575
|
};
|
|
1590
2576
|
_OpenHiGlobalService.SERVICE_TYPE = "global";
|
|
1591
2577
|
var OpenHiGlobalService = _OpenHiGlobalService;
|
|
1592
2578
|
|
|
1593
|
-
// src/
|
|
1594
|
-
var
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
2579
|
+
// src/workflows/control-plane/seed-demo-data/events.ts
|
|
2580
|
+
var import_types = require("@openhi/types");
|
|
2581
|
+
var import_workflows2 = __toESM(require_lib2());
|
|
2582
|
+
var SEED_DEMO_DATA_CONSUMER_NAME = "seed-demo-data";
|
|
2583
|
+
var DEMO_URN_SYSTEM = "urn:openhi:demo";
|
|
2584
|
+
var OPENHI_RESOURCE_URN_SYSTEM = "http://openhi.org/";
|
|
2585
|
+
var DEMO_PERIOD = { start: "2026-01-01T00:00:00Z" };
|
|
2586
|
+
var PLATFORM_SCOPE_TENANT_ID = "platform";
|
|
2587
|
+
var PLACEHOLDER_TENANT_ID = "placeholder-tenant-id";
|
|
2588
|
+
var PLACEHOLDER_WORKSPACE_ID = "placeholder-workspace-id";
|
|
2589
|
+
var DEV_USERS = [
|
|
2590
|
+
{ id: "dev-russell", email: "russell@codedrifters.com" },
|
|
2591
|
+
{ id: "dev-cameron", email: "cameron@codedrifters.com" },
|
|
2592
|
+
{ id: "dev-neelima", email: "neelima@codedrifters.com" },
|
|
2593
|
+
{ id: "dev-garon", email: "garon@codedrifters.com" },
|
|
2594
|
+
{ id: "dev-dave", email: "dave@codedrifters.com" },
|
|
2595
|
+
{ id: "dev-drew", email: "drew@codedrifters.com" },
|
|
2596
|
+
{ id: "dev-jessica", email: "jessica@codedrifters.com" },
|
|
2597
|
+
{ id: "dev-jared", email: "jared@codedrifters.com" },
|
|
2598
|
+
{ id: "dev-goddess", email: "goddess@codedrifters.com" }
|
|
2599
|
+
];
|
|
2600
|
+
var DEMO_TENANT_SPECS = [
|
|
2601
|
+
{
|
|
2602
|
+
scenario: "placeholder",
|
|
2603
|
+
tenantId: PLACEHOLDER_TENANT_ID,
|
|
2604
|
+
tenantName: "OpenHI Placeholder Tenant",
|
|
2605
|
+
workspaces: [
|
|
2606
|
+
{
|
|
2607
|
+
id: PLACEHOLDER_WORKSPACE_ID,
|
|
2608
|
+
name: "OpenHI Placeholder Workspace",
|
|
2609
|
+
roleSuffix: "workspace"
|
|
2610
|
+
}
|
|
2611
|
+
]
|
|
2612
|
+
},
|
|
2613
|
+
{
|
|
2614
|
+
scenario: "demo-wound-care",
|
|
2615
|
+
tenantId: "demo-wound-care-tenant",
|
|
2616
|
+
tenantName: "Cedarbrook Wound Healing Institute",
|
|
2617
|
+
workspaces: [
|
|
2618
|
+
{
|
|
2619
|
+
id: "demo-wound-care-workspace",
|
|
2620
|
+
name: "Cedarbrook Outpatient Wound Clinic",
|
|
2621
|
+
roleSuffix: "workspace"
|
|
2622
|
+
}
|
|
2623
|
+
]
|
|
2624
|
+
},
|
|
2625
|
+
{
|
|
2626
|
+
scenario: "demo-primary-care",
|
|
2627
|
+
tenantId: "demo-primary-care-tenant",
|
|
2628
|
+
tenantName: "Maple Ridge Family Medicine",
|
|
2629
|
+
workspaces: [
|
|
2630
|
+
{
|
|
2631
|
+
id: "demo-primary-care-workspace",
|
|
2632
|
+
name: "Maple Ridge Main Street Office",
|
|
2633
|
+
roleSuffix: "workspace"
|
|
2634
|
+
}
|
|
2635
|
+
]
|
|
2636
|
+
},
|
|
2637
|
+
{
|
|
2638
|
+
scenario: "demo-mixed",
|
|
2639
|
+
tenantId: "demo-mixed-tenant",
|
|
2640
|
+
tenantName: "Northbridge Health Network",
|
|
2641
|
+
workspaces: [
|
|
2642
|
+
{
|
|
2643
|
+
id: "demo-mixed-workspace-wound-care",
|
|
2644
|
+
name: "Northbridge Wound Care Center",
|
|
2645
|
+
roleSuffix: "workspace-wound-care"
|
|
2646
|
+
},
|
|
2647
|
+
{
|
|
2648
|
+
id: "demo-mixed-workspace-primary-care",
|
|
2649
|
+
name: "Northbridge Family Practice",
|
|
2650
|
+
roleSuffix: "workspace-primary-care"
|
|
2651
|
+
}
|
|
2652
|
+
]
|
|
2653
|
+
}
|
|
2654
|
+
];
|
|
2655
|
+
var demoMembershipId = (devUserId, tenantId) => `demo-membership-${devUserId}-${tenantId}`;
|
|
2656
|
+
var demoRoleAssignmentId = (devUserId, tenantId, roleCode) => `demo-roleassignment-${devUserId}-${tenantId}-${roleCode}`;
|
|
2657
|
+
var demoScenarioIdentifier = (scenario, roleSuffix) => ({
|
|
2658
|
+
system: DEMO_URN_SYSTEM,
|
|
2659
|
+
value: `${scenario}:${roleSuffix}`
|
|
2660
|
+
});
|
|
2661
|
+
var openhiResourceIdentifier = (params) => ({
|
|
2662
|
+
use: "unversioned",
|
|
2663
|
+
system: OPENHI_RESOURCE_URN_SYSTEM,
|
|
2664
|
+
value: `urn:ohi:${params.tenantId}:${params.workspaceId}:${params.resourceType}:${params.id}`
|
|
2665
|
+
});
|
|
2666
|
+
var demoRolesForUserInTenant = (_user, _tenantId) => {
|
|
2667
|
+
void _user;
|
|
2668
|
+
void _tenantId;
|
|
2669
|
+
return [import_types.PLATFORM_ROLE_CODE.TENANT_ADMIN];
|
|
2670
|
+
};
|
|
2671
|
+
var rolePartitionKey = (roleId) => `role#id#${roleId}`;
|
|
2672
|
+
var demoTenantPartitionKey = (tenantId) => `tenant#id#${tenantId}`;
|
|
2673
|
+
var demoWorkspacePartitionKey = (tenantId, workspaceId) => `tid#${tenantId}#workspace#id#${workspaceId}`;
|
|
2674
|
+
var demoMembershipPartitionKey = (tenantId, membershipId) => `tid#${tenantId}#membership#id#${membershipId}`;
|
|
2675
|
+
var demoRoleAssignmentPartitionKey = (tenantId, roleAssignmentId) => `tid#${tenantId}#roleassignment#id#${roleAssignmentId}`;
|
|
2676
|
+
var demoUserPartitionKey = (userId) => `user#id#${userId}`;
|
|
2677
|
+
var demoBasePartitionKeys = () => {
|
|
2678
|
+
const keys = [];
|
|
2679
|
+
for (const spec of DEMO_TENANT_SPECS) {
|
|
2680
|
+
keys.push(demoTenantPartitionKey(spec.tenantId));
|
|
2681
|
+
for (const workspace of spec.workspaces) {
|
|
2682
|
+
keys.push(demoWorkspacePartitionKey(spec.tenantId, workspace.id));
|
|
2683
|
+
}
|
|
1600
2684
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
2685
|
+
return keys;
|
|
2686
|
+
};
|
|
2687
|
+
var demoDevUserPartitionKeys = (devUsers) => {
|
|
2688
|
+
const keys = [];
|
|
2689
|
+
for (const user of devUsers) {
|
|
2690
|
+
keys.push(demoUserPartitionKey(user.id));
|
|
2691
|
+
for (const spec of DEMO_TENANT_SPECS) {
|
|
2692
|
+
keys.push(
|
|
2693
|
+
demoMembershipPartitionKey(
|
|
2694
|
+
spec.tenantId,
|
|
2695
|
+
demoMembershipId(user.id, spec.tenantId)
|
|
2696
|
+
)
|
|
2697
|
+
);
|
|
2698
|
+
for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {
|
|
2699
|
+
keys.push(
|
|
2700
|
+
demoRoleAssignmentPartitionKey(
|
|
2701
|
+
spec.tenantId,
|
|
2702
|
+
demoRoleAssignmentId(user.id, spec.tenantId, roleCode)
|
|
2703
|
+
)
|
|
2704
|
+
);
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
keys.push(
|
|
2708
|
+
demoRoleAssignmentPartitionKey(
|
|
2709
|
+
PLATFORM_SCOPE_TENANT_ID,
|
|
2710
|
+
demoRoleAssignmentId(
|
|
2711
|
+
user.id,
|
|
2712
|
+
PLATFORM_SCOPE_TENANT_ID,
|
|
2713
|
+
import_types.PLATFORM_ROLE_CODE.SYSTEM_ADMIN
|
|
2714
|
+
)
|
|
2715
|
+
)
|
|
2716
|
+
);
|
|
1603
2717
|
}
|
|
2718
|
+
return keys;
|
|
2719
|
+
};
|
|
2720
|
+
|
|
2721
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data-lambda.ts
|
|
2722
|
+
var import_node_fs7 = __toESM(require("fs"));
|
|
2723
|
+
var import_node_path7 = __toESM(require("path"));
|
|
2724
|
+
var import_types8 = require("@openhi/types");
|
|
2725
|
+
var import_aws_cdk_lib12 = require("aws-cdk-lib");
|
|
2726
|
+
var import_aws_events6 = require("aws-cdk-lib/aws-events");
|
|
2727
|
+
var import_aws_events_targets2 = require("aws-cdk-lib/aws-events-targets");
|
|
2728
|
+
var import_aws_iam3 = require("aws-cdk-lib/aws-iam");
|
|
2729
|
+
var import_aws_lambda7 = require("aws-cdk-lib/aws-lambda");
|
|
2730
|
+
var import_aws_lambda_nodejs7 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
2731
|
+
var import_constructs11 = require("constructs");
|
|
2732
|
+
|
|
2733
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts
|
|
2734
|
+
var import_node_crypto = require("crypto");
|
|
2735
|
+
var import_client_cognito_identity_provider = require("@aws-sdk/client-cognito-identity-provider");
|
|
2736
|
+
var import_client_dynamodb2 = require("@aws-sdk/client-dynamodb");
|
|
2737
|
+
var import_types7 = require("@openhi/types");
|
|
2738
|
+
var import_workflows3 = __toESM(require_lib2());
|
|
2739
|
+
|
|
2740
|
+
// src/data/dynamo/dynamo-control-service.ts
|
|
2741
|
+
var import_electrodb8 = require("electrodb");
|
|
2742
|
+
|
|
2743
|
+
// src/data/dynamo/dynamo-client.ts
|
|
2744
|
+
var import_client_dynamodb = require("@aws-sdk/client-dynamodb");
|
|
2745
|
+
var defaultTableName = process.env.DYNAMO_TABLE_NAME ?? "jesttesttable";
|
|
2746
|
+
var dynamoClient = new import_client_dynamodb.DynamoDBClient({
|
|
2747
|
+
...process.env.MOCK_DYNAMODB_ENDPOINT && {
|
|
2748
|
+
endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
|
|
2749
|
+
sslEnabled: false,
|
|
2750
|
+
region: "local"
|
|
2751
|
+
}
|
|
2752
|
+
});
|
|
2753
|
+
|
|
2754
|
+
// src/data/dynamo/entities/control/configuration-entity.ts
|
|
2755
|
+
var import_electrodb = require("electrodb");
|
|
2756
|
+
|
|
2757
|
+
// src/data/dynamo/entities/control/control-entity-common.ts
|
|
2758
|
+
var import_types2 = require("@openhi/types");
|
|
2759
|
+
|
|
2760
|
+
// src/data/dynamo/shard.ts
|
|
2761
|
+
var SHARD_COUNT = 4;
|
|
2762
|
+
function computeShard(id) {
|
|
2763
|
+
let hash = 2166136261;
|
|
2764
|
+
for (let i = 0; i < id.length; i++) {
|
|
2765
|
+
hash ^= id.charCodeAt(i);
|
|
2766
|
+
hash = Math.imul(hash, 16777619);
|
|
2767
|
+
}
|
|
2768
|
+
return (hash >>> 0) % SHARD_COUNT;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
// src/data/dynamo/entities/control/control-entity-common.ts
|
|
2772
|
+
var gsi1ShardAttribute = {
|
|
2773
|
+
type: "string",
|
|
2774
|
+
watch: ["id"],
|
|
2775
|
+
set: (_val, item) => {
|
|
2776
|
+
if (typeof item?.id !== "string" || item.id.length === 0) {
|
|
2777
|
+
return void 0;
|
|
2778
|
+
}
|
|
2779
|
+
return String(computeShard(item.id));
|
|
2780
|
+
}
|
|
2781
|
+
};
|
|
2782
|
+
var gsi1skAttribute = {
|
|
2783
|
+
type: "string",
|
|
2784
|
+
watch: ["resource", "lastUpdated", "id"],
|
|
2785
|
+
set: (_val, item) => {
|
|
2786
|
+
const id = typeof item?.id === "string" ? item.id : "";
|
|
2787
|
+
const lastUpdated = typeof item?.lastUpdated === "string" ? item.lastUpdated : "";
|
|
2788
|
+
const fallback = `${lastUpdated}#${id}`;
|
|
2789
|
+
if (typeof item?.resource !== "string" || item.resource.length === 0) {
|
|
2790
|
+
return fallback;
|
|
2791
|
+
}
|
|
2792
|
+
let parsed;
|
|
2793
|
+
try {
|
|
2794
|
+
parsed = JSON.parse(item.resource);
|
|
2795
|
+
} catch {
|
|
2796
|
+
return fallback;
|
|
2797
|
+
}
|
|
2798
|
+
if (!parsed || typeof parsed !== "object") return fallback;
|
|
2799
|
+
const resourceType = parsed.resourceType;
|
|
2800
|
+
if (typeof resourceType !== "string") return fallback;
|
|
2801
|
+
const label = (0, import_types2.extractLabel)(parsed);
|
|
2802
|
+
return label !== void 0 ? `${label}#${id}` : fallback;
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
|
|
2806
|
+
// src/data/dynamo/entities/control/configuration-entity.ts
|
|
2807
|
+
var ConfigurationEntity = new import_electrodb.Entity({
|
|
2808
|
+
model: {
|
|
2809
|
+
entity: "configuration",
|
|
2810
|
+
service: "control",
|
|
2811
|
+
version: "01"
|
|
2812
|
+
},
|
|
2813
|
+
attributes: {
|
|
2814
|
+
/** Sort key. "CURRENT" for current version; version history in S3. */
|
|
2815
|
+
sk: {
|
|
2816
|
+
type: "string",
|
|
2817
|
+
required: true,
|
|
2818
|
+
default: "CURRENT"
|
|
2819
|
+
},
|
|
2820
|
+
/** Tenant scope. Use "BASELINE" when the config is baseline default (no tenant). */
|
|
2821
|
+
tenantId: {
|
|
2822
|
+
type: "string",
|
|
2823
|
+
required: true,
|
|
2824
|
+
default: "BASELINE"
|
|
2825
|
+
},
|
|
2826
|
+
/** Workspace scope. Use "-" when absent. */
|
|
2827
|
+
workspaceId: {
|
|
2828
|
+
type: "string",
|
|
2829
|
+
required: true,
|
|
2830
|
+
default: "-"
|
|
2831
|
+
},
|
|
2832
|
+
/** User scope. Use "-" when absent. */
|
|
2833
|
+
userId: {
|
|
2834
|
+
type: "string",
|
|
2835
|
+
required: true,
|
|
2836
|
+
default: "-"
|
|
2837
|
+
},
|
|
2838
|
+
/** Role scope. Use "-" when absent. */
|
|
2839
|
+
roleId: {
|
|
2840
|
+
type: "string",
|
|
2841
|
+
required: true,
|
|
2842
|
+
default: "-"
|
|
2843
|
+
},
|
|
2844
|
+
/** Config type (category), e.g. endpoints, branding, display. */
|
|
2845
|
+
key: {
|
|
2846
|
+
type: "string",
|
|
2847
|
+
required: true
|
|
2848
|
+
},
|
|
2849
|
+
/** FHIR Resource.id; logical id in URL and for the Configuration resource. */
|
|
2850
|
+
id: {
|
|
2851
|
+
type: "string",
|
|
2852
|
+
required: true
|
|
2853
|
+
},
|
|
2854
|
+
/** Payload as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */
|
|
2855
|
+
resource: {
|
|
2856
|
+
type: "string",
|
|
2857
|
+
required: true
|
|
2858
|
+
},
|
|
2859
|
+
/**
|
|
2860
|
+
* Summary projection (key display fields as JSON string: id, key, status).
|
|
2861
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
2862
|
+
*/
|
|
2863
|
+
summary: {
|
|
2864
|
+
type: "string",
|
|
2865
|
+
required: true
|
|
2866
|
+
},
|
|
2867
|
+
/** Version id (e.g. ULID). Tracks current version; S3 history key. */
|
|
2868
|
+
vid: {
|
|
2869
|
+
type: "string",
|
|
2870
|
+
required: true
|
|
2871
|
+
},
|
|
2872
|
+
lastUpdated: {
|
|
2873
|
+
type: "string",
|
|
2874
|
+
required: true
|
|
2875
|
+
},
|
|
2876
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
2877
|
+
deleted: {
|
|
2878
|
+
type: "boolean",
|
|
2879
|
+
required: false
|
|
2880
|
+
},
|
|
2881
|
+
bundleId: {
|
|
2882
|
+
type: "string",
|
|
2883
|
+
required: false
|
|
2884
|
+
},
|
|
2885
|
+
msgId: {
|
|
2886
|
+
type: "string",
|
|
2887
|
+
required: false
|
|
2888
|
+
}
|
|
2889
|
+
},
|
|
2890
|
+
indexes: {
|
|
2891
|
+
/** Base table: PK, SK (data store key names). PK is built from tenantId, workspaceId, userId, roleId; SK is built from key and sk. Do not supply PK or SK from outside. */
|
|
2892
|
+
record: {
|
|
2893
|
+
pk: {
|
|
2894
|
+
field: "PK",
|
|
2895
|
+
composite: ["tenantId", "workspaceId", "userId", "roleId"],
|
|
2896
|
+
template: "CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}"
|
|
2897
|
+
},
|
|
2898
|
+
sk: {
|
|
2899
|
+
field: "SK",
|
|
2900
|
+
composite: ["key", "sk"],
|
|
2901
|
+
template: "KEY#${key}#SK#${sk}"
|
|
2902
|
+
}
|
|
2903
|
+
},
|
|
2904
|
+
/**
|
|
2905
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Configuration entries for a
|
|
2906
|
+
* (tenant, workspace) across the four shards. Use for "list configs scoped to this tenant"
|
|
2907
|
+
* (workspaceId = "-") or "list configs scoped to this workspace". Does not support
|
|
2908
|
+
* hierarchical resolution in one query; use base table GetItem in fallback order
|
|
2909
|
+
* (user → workspace → tenant → baseline) for that.
|
|
2910
|
+
* SK is `<key>#<id>` — Configuration's `key` is a required entity attribute (the
|
|
2911
|
+
* config category: endpoints, branding, display, …) and the natural sort/lookup
|
|
2912
|
+
* dimension. `casing: "none"` preserves the literal key value.
|
|
2913
|
+
*/
|
|
2914
|
+
gsi1: {
|
|
2915
|
+
index: "GSI1",
|
|
2916
|
+
pk: {
|
|
2917
|
+
field: "GSI1PK",
|
|
2918
|
+
composite: ["tenantId", "workspaceId", "gsi1Shard"],
|
|
2919
|
+
template: "TID#${tenantId}#WID#${workspaceId}#RT#Configuration#SHARD#${gsi1Shard}"
|
|
2920
|
+
},
|
|
2921
|
+
sk: {
|
|
2922
|
+
field: "GSI1SK",
|
|
2923
|
+
casing: "none",
|
|
2924
|
+
composite: ["key", "id"],
|
|
2925
|
+
template: "${key}#${id}"
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
});
|
|
2930
|
+
|
|
2931
|
+
// src/data/dynamo/entities/control/membership-entity.ts
|
|
2932
|
+
var import_electrodb2 = require("electrodb");
|
|
2933
|
+
var MembershipEntity = new import_electrodb2.Entity({
|
|
2934
|
+
model: {
|
|
2935
|
+
entity: "membership",
|
|
2936
|
+
service: "control",
|
|
2937
|
+
version: "01"
|
|
2938
|
+
},
|
|
2939
|
+
attributes: {
|
|
2940
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
2941
|
+
sk: {
|
|
2942
|
+
type: "string",
|
|
2943
|
+
required: true,
|
|
2944
|
+
default: "CURRENT"
|
|
2945
|
+
},
|
|
2946
|
+
/** Tenant in which the user has membership (required). */
|
|
2947
|
+
tenantId: {
|
|
2948
|
+
type: "string",
|
|
2949
|
+
required: true
|
|
2950
|
+
},
|
|
2951
|
+
/** FHIR Resource.id; membership id. */
|
|
2952
|
+
id: {
|
|
2953
|
+
type: "string",
|
|
2954
|
+
required: true
|
|
2955
|
+
},
|
|
2956
|
+
/** Full Membership resource serialized as JSON string. */
|
|
2957
|
+
resource: {
|
|
2958
|
+
type: "string",
|
|
2959
|
+
required: true
|
|
2960
|
+
},
|
|
2961
|
+
/**
|
|
2962
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
2963
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
2964
|
+
*/
|
|
2965
|
+
summary: {
|
|
2966
|
+
type: "string",
|
|
2967
|
+
required: true
|
|
2968
|
+
},
|
|
2969
|
+
/** Version id (e.g. ULID). */
|
|
2970
|
+
vid: {
|
|
2971
|
+
type: "string",
|
|
2972
|
+
required: true
|
|
2973
|
+
},
|
|
2974
|
+
lastUpdated: {
|
|
2975
|
+
type: "string",
|
|
2976
|
+
required: true
|
|
2977
|
+
},
|
|
2978
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
2979
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
2980
|
+
gsi1sk: gsi1skAttribute,
|
|
2981
|
+
deleted: {
|
|
2982
|
+
type: "boolean",
|
|
2983
|
+
required: false
|
|
2984
|
+
},
|
|
2985
|
+
bundleId: {
|
|
2986
|
+
type: "string",
|
|
2987
|
+
required: false
|
|
2988
|
+
},
|
|
2989
|
+
msgId: {
|
|
2990
|
+
type: "string",
|
|
2991
|
+
required: false
|
|
2992
|
+
}
|
|
2993
|
+
},
|
|
2994
|
+
indexes: {
|
|
2995
|
+
/** Base table: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
2996
|
+
record: {
|
|
2997
|
+
pk: {
|
|
2998
|
+
field: "PK",
|
|
2999
|
+
composite: ["tenantId", "id"],
|
|
3000
|
+
template: "TID#${tenantId}#MEMBERSHIP#ID#${id}"
|
|
3001
|
+
},
|
|
3002
|
+
sk: {
|
|
3003
|
+
field: "SK",
|
|
3004
|
+
composite: ["sk"],
|
|
3005
|
+
template: "${sk}"
|
|
3006
|
+
}
|
|
3007
|
+
},
|
|
3008
|
+
/**
|
|
3009
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the
|
|
3010
|
+
* four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.
|
|
3011
|
+
* SK is derived via `gsi1skAttribute` — uses the resource's natural label when
|
|
3012
|
+
* extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
|
|
3013
|
+
* normalized label and ISO-8601 `T`/`Z`.
|
|
3014
|
+
*/
|
|
3015
|
+
gsi1: {
|
|
3016
|
+
index: "GSI1",
|
|
3017
|
+
pk: {
|
|
3018
|
+
field: "GSI1PK",
|
|
3019
|
+
composite: ["tenantId", "gsi1Shard"],
|
|
3020
|
+
template: "TID#${tenantId}#WID#-#RT#Membership#SHARD#${gsi1Shard}"
|
|
3021
|
+
},
|
|
3022
|
+
sk: {
|
|
3023
|
+
field: "GSI1SK",
|
|
3024
|
+
casing: "none",
|
|
3025
|
+
composite: ["gsi1sk"],
|
|
3026
|
+
template: "${gsi1sk}"
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
});
|
|
3031
|
+
|
|
3032
|
+
// src/data/dynamo/entities/control/role-entity.ts
|
|
3033
|
+
var import_electrodb3 = require("electrodb");
|
|
3034
|
+
var RoleEntity = new import_electrodb3.Entity({
|
|
3035
|
+
model: {
|
|
3036
|
+
entity: "role",
|
|
3037
|
+
service: "control",
|
|
3038
|
+
version: "01"
|
|
3039
|
+
},
|
|
3040
|
+
attributes: {
|
|
3041
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3042
|
+
sk: {
|
|
3043
|
+
type: "string",
|
|
3044
|
+
required: true,
|
|
3045
|
+
default: "CURRENT"
|
|
3046
|
+
},
|
|
3047
|
+
/** FHIR Resource.id; role id. */
|
|
3048
|
+
id: {
|
|
3049
|
+
type: "string",
|
|
3050
|
+
required: true
|
|
3051
|
+
},
|
|
3052
|
+
/** Full Role resource serialized as JSON string. */
|
|
3053
|
+
resource: {
|
|
3054
|
+
type: "string",
|
|
3055
|
+
required: true
|
|
3056
|
+
},
|
|
3057
|
+
/**
|
|
3058
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3059
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3060
|
+
*/
|
|
3061
|
+
summary: {
|
|
3062
|
+
type: "string",
|
|
3063
|
+
required: true
|
|
3064
|
+
},
|
|
3065
|
+
/** Version id (e.g. ULID). */
|
|
3066
|
+
vid: {
|
|
3067
|
+
type: "string",
|
|
3068
|
+
required: true
|
|
3069
|
+
},
|
|
3070
|
+
lastUpdated: {
|
|
3071
|
+
type: "string",
|
|
3072
|
+
required: true
|
|
3073
|
+
},
|
|
3074
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3075
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3076
|
+
gsi1sk: gsi1skAttribute,
|
|
3077
|
+
deleted: {
|
|
3078
|
+
type: "boolean",
|
|
3079
|
+
required: false
|
|
3080
|
+
},
|
|
3081
|
+
bundleId: {
|
|
3082
|
+
type: "string",
|
|
3083
|
+
required: false
|
|
3084
|
+
},
|
|
3085
|
+
msgId: {
|
|
3086
|
+
type: "string",
|
|
3087
|
+
required: false
|
|
3088
|
+
}
|
|
3089
|
+
},
|
|
3090
|
+
indexes: {
|
|
3091
|
+
/** Base table: PK = ROLE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3092
|
+
record: {
|
|
3093
|
+
pk: {
|
|
3094
|
+
field: "PK",
|
|
3095
|
+
composite: ["id"],
|
|
3096
|
+
template: "ROLE#ID#${id}"
|
|
3097
|
+
},
|
|
3098
|
+
sk: {
|
|
3099
|
+
field: "SK",
|
|
3100
|
+
composite: ["sk"],
|
|
3101
|
+
template: "${sk}"
|
|
3102
|
+
}
|
|
3103
|
+
},
|
|
3104
|
+
/**
|
|
3105
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.
|
|
3106
|
+
* Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.
|
|
3107
|
+
* SK is derived via `gsi1skAttribute` — uses the resource's natural label when
|
|
3108
|
+
* extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
|
|
3109
|
+
* normalized label and ISO-8601 `T`/`Z`.
|
|
3110
|
+
*/
|
|
3111
|
+
gsi1: {
|
|
3112
|
+
index: "GSI1",
|
|
3113
|
+
pk: {
|
|
3114
|
+
field: "GSI1PK",
|
|
3115
|
+
composite: ["gsi1Shard"],
|
|
3116
|
+
template: "TID#-#WID#-#RT#Role#SHARD#${gsi1Shard}"
|
|
3117
|
+
},
|
|
3118
|
+
sk: {
|
|
3119
|
+
field: "GSI1SK",
|
|
3120
|
+
casing: "none",
|
|
3121
|
+
composite: ["gsi1sk"],
|
|
3122
|
+
template: "${gsi1sk}"
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
});
|
|
3127
|
+
|
|
3128
|
+
// src/data/dynamo/entities/control/roleassignment-entity.ts
|
|
3129
|
+
var import_electrodb4 = require("electrodb");
|
|
3130
|
+
var RoleAssignmentEntity = new import_electrodb4.Entity({
|
|
3131
|
+
model: {
|
|
3132
|
+
entity: "roleassignment",
|
|
3133
|
+
service: "control",
|
|
3134
|
+
version: "01"
|
|
3135
|
+
},
|
|
3136
|
+
attributes: {
|
|
3137
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3138
|
+
sk: {
|
|
3139
|
+
type: "string",
|
|
3140
|
+
required: true,
|
|
3141
|
+
default: "CURRENT"
|
|
3142
|
+
},
|
|
3143
|
+
/** Tenant in which the role assignment applies (required). */
|
|
3144
|
+
tenantId: {
|
|
3145
|
+
type: "string",
|
|
3146
|
+
required: true
|
|
3147
|
+
},
|
|
3148
|
+
/** FHIR Resource.id; role assignment id. */
|
|
3149
|
+
id: {
|
|
3150
|
+
type: "string",
|
|
3151
|
+
required: true
|
|
3152
|
+
},
|
|
3153
|
+
/** Full RoleAssignment resource serialized as JSON string. */
|
|
3154
|
+
resource: {
|
|
3155
|
+
type: "string",
|
|
3156
|
+
required: true
|
|
3157
|
+
},
|
|
3158
|
+
/**
|
|
3159
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3160
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3161
|
+
*/
|
|
3162
|
+
summary: {
|
|
3163
|
+
type: "string",
|
|
3164
|
+
required: true
|
|
3165
|
+
},
|
|
3166
|
+
/** Version id (e.g. ULID). */
|
|
3167
|
+
vid: {
|
|
3168
|
+
type: "string",
|
|
3169
|
+
required: true
|
|
3170
|
+
},
|
|
3171
|
+
lastUpdated: {
|
|
3172
|
+
type: "string",
|
|
3173
|
+
required: true
|
|
3174
|
+
},
|
|
3175
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3176
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3177
|
+
gsi1sk: gsi1skAttribute,
|
|
3178
|
+
deleted: {
|
|
3179
|
+
type: "boolean",
|
|
3180
|
+
required: false
|
|
3181
|
+
},
|
|
3182
|
+
bundleId: {
|
|
3183
|
+
type: "string",
|
|
3184
|
+
required: false
|
|
3185
|
+
},
|
|
3186
|
+
msgId: {
|
|
3187
|
+
type: "string",
|
|
3188
|
+
required: false
|
|
3189
|
+
}
|
|
3190
|
+
},
|
|
3191
|
+
indexes: {
|
|
3192
|
+
/** Base table: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3193
|
+
record: {
|
|
3194
|
+
pk: {
|
|
3195
|
+
field: "PK",
|
|
3196
|
+
composite: ["tenantId", "id"],
|
|
3197
|
+
template: "TID#${tenantId}#ROLEASSIGNMENT#ID#${id}"
|
|
3198
|
+
},
|
|
3199
|
+
sk: {
|
|
3200
|
+
field: "SK",
|
|
3201
|
+
composite: ["sk"],
|
|
3202
|
+
template: "${sk}"
|
|
3203
|
+
}
|
|
3204
|
+
},
|
|
3205
|
+
/**
|
|
3206
|
+
* GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the
|
|
3207
|
+
* four shards. Tenant-scoped only, so `WID#-` is a sentinel.
|
|
3208
|
+
* SK is derived via `gsi1skAttribute` — uses the resource's natural label when
|
|
3209
|
+
* extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
|
|
3210
|
+
* normalized label and ISO-8601 `T`/`Z`.
|
|
3211
|
+
*/
|
|
3212
|
+
gsi1: {
|
|
3213
|
+
index: "GSI1",
|
|
3214
|
+
pk: {
|
|
3215
|
+
field: "GSI1PK",
|
|
3216
|
+
composite: ["tenantId", "gsi1Shard"],
|
|
3217
|
+
template: "TID#${tenantId}#WID#-#RT#RoleAssignment#SHARD#${gsi1Shard}"
|
|
3218
|
+
},
|
|
3219
|
+
sk: {
|
|
3220
|
+
field: "GSI1SK",
|
|
3221
|
+
casing: "none",
|
|
3222
|
+
composite: ["gsi1sk"],
|
|
3223
|
+
template: "${gsi1sk}"
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
});
|
|
3228
|
+
|
|
3229
|
+
// src/data/dynamo/entities/control/tenant-entity.ts
|
|
3230
|
+
var import_electrodb5 = require("electrodb");
|
|
3231
|
+
var TenantEntity = new import_electrodb5.Entity({
|
|
3232
|
+
model: {
|
|
3233
|
+
entity: "tenant",
|
|
3234
|
+
service: "control",
|
|
3235
|
+
version: "01"
|
|
3236
|
+
},
|
|
3237
|
+
attributes: {
|
|
3238
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3239
|
+
sk: {
|
|
3240
|
+
type: "string",
|
|
3241
|
+
required: true,
|
|
3242
|
+
default: "CURRENT"
|
|
3243
|
+
},
|
|
3244
|
+
/** The tenant's own id (= resource id). Drives the partition key. */
|
|
3245
|
+
tenantId: {
|
|
3246
|
+
type: "string",
|
|
3247
|
+
required: true
|
|
3248
|
+
},
|
|
3249
|
+
/** FHIR Resource.id; logical id in URL. Equals tenantId. */
|
|
3250
|
+
id: {
|
|
3251
|
+
type: "string",
|
|
3252
|
+
required: true
|
|
3253
|
+
},
|
|
3254
|
+
/** Full Tenant resource serialized as JSON string. */
|
|
3255
|
+
resource: {
|
|
3256
|
+
type: "string",
|
|
3257
|
+
required: true
|
|
3258
|
+
},
|
|
3259
|
+
/**
|
|
3260
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3261
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3262
|
+
*/
|
|
3263
|
+
summary: {
|
|
3264
|
+
type: "string",
|
|
3265
|
+
required: true
|
|
3266
|
+
},
|
|
3267
|
+
/** Version id (e.g. ULID). */
|
|
3268
|
+
vid: {
|
|
3269
|
+
type: "string",
|
|
3270
|
+
required: true
|
|
3271
|
+
},
|
|
3272
|
+
lastUpdated: {
|
|
3273
|
+
type: "string",
|
|
3274
|
+
required: true
|
|
3275
|
+
},
|
|
3276
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3277
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3278
|
+
gsi1sk: gsi1skAttribute,
|
|
3279
|
+
deleted: {
|
|
3280
|
+
type: "boolean",
|
|
3281
|
+
required: false
|
|
3282
|
+
},
|
|
3283
|
+
bundleId: {
|
|
3284
|
+
type: "string",
|
|
3285
|
+
required: false
|
|
3286
|
+
},
|
|
3287
|
+
msgId: {
|
|
3288
|
+
type: "string",
|
|
3289
|
+
required: false
|
|
3290
|
+
}
|
|
3291
|
+
},
|
|
3292
|
+
indexes: {
|
|
3293
|
+
/** Base table: PK = TENANT#ID#<tenantId>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3294
|
+
record: {
|
|
3295
|
+
pk: {
|
|
3296
|
+
field: "PK",
|
|
3297
|
+
composite: ["tenantId"],
|
|
3298
|
+
template: "TENANT#ID#${tenantId}"
|
|
3299
|
+
},
|
|
3300
|
+
sk: {
|
|
3301
|
+
field: "SK",
|
|
3302
|
+
composite: ["sk"],
|
|
3303
|
+
template: "${sk}"
|
|
3304
|
+
}
|
|
3305
|
+
},
|
|
3306
|
+
/**
|
|
3307
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.
|
|
3308
|
+
* Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`
|
|
3309
|
+
* sentinels precede `RT#Tenant#SHARD#<n>`. SK is derived via `gsi1skAttribute` —
|
|
3310
|
+
* `<normalizedName>#<id>` when the resource carries a `name`, else `<lastUpdated>#<id>`
|
|
3311
|
+
* (DR-004). `casing: "none"` preserves the normalized label and ISO-8601 `T`/`Z`.
|
|
3312
|
+
*/
|
|
3313
|
+
gsi1: {
|
|
3314
|
+
index: "GSI1",
|
|
3315
|
+
pk: {
|
|
3316
|
+
field: "GSI1PK",
|
|
3317
|
+
composite: ["gsi1Shard"],
|
|
3318
|
+
template: "TID#-#WID#-#RT#Tenant#SHARD#${gsi1Shard}"
|
|
3319
|
+
},
|
|
3320
|
+
sk: {
|
|
3321
|
+
field: "GSI1SK",
|
|
3322
|
+
casing: "none",
|
|
3323
|
+
composite: ["gsi1sk"],
|
|
3324
|
+
template: "${gsi1sk}"
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
});
|
|
3329
|
+
|
|
3330
|
+
// src/data/dynamo/entities/control/user-entity.ts
|
|
3331
|
+
var import_electrodb6 = require("electrodb");
|
|
3332
|
+
var UserEntity = new import_electrodb6.Entity({
|
|
3333
|
+
model: {
|
|
3334
|
+
entity: "user",
|
|
3335
|
+
service: "control",
|
|
3336
|
+
version: "01"
|
|
3337
|
+
},
|
|
3338
|
+
attributes: {
|
|
3339
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3340
|
+
sk: {
|
|
3341
|
+
type: "string",
|
|
3342
|
+
required: true,
|
|
3343
|
+
default: "CURRENT"
|
|
3344
|
+
},
|
|
3345
|
+
/** FHIR Resource.id; platform user id (ohi_uid). */
|
|
3346
|
+
id: {
|
|
3347
|
+
type: "string",
|
|
3348
|
+
required: true
|
|
3349
|
+
},
|
|
3350
|
+
/** Full User resource serialized as JSON string. */
|
|
3351
|
+
resource: {
|
|
3352
|
+
type: "string",
|
|
3353
|
+
required: true
|
|
3354
|
+
},
|
|
3355
|
+
/**
|
|
3356
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3357
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3358
|
+
*/
|
|
3359
|
+
summary: {
|
|
3360
|
+
type: "string",
|
|
3361
|
+
required: true
|
|
3362
|
+
},
|
|
3363
|
+
/**
|
|
3364
|
+
* Immutable Cognito-issued `sub` claim. Drives GSI2 (sub-lookup). Optional until the
|
|
3365
|
+
* Post Confirmation Lambda (#770) lands; required thereafter.
|
|
3366
|
+
*/
|
|
3367
|
+
cognitoSub: {
|
|
3368
|
+
type: "string",
|
|
3369
|
+
required: false
|
|
3370
|
+
},
|
|
3371
|
+
/** Version id (e.g. ULID). */
|
|
3372
|
+
vid: {
|
|
3373
|
+
type: "string",
|
|
3374
|
+
required: true
|
|
3375
|
+
},
|
|
3376
|
+
lastUpdated: {
|
|
3377
|
+
type: "string",
|
|
3378
|
+
required: true
|
|
3379
|
+
},
|
|
3380
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3381
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3382
|
+
gsi1sk: gsi1skAttribute,
|
|
3383
|
+
deleted: {
|
|
3384
|
+
type: "boolean",
|
|
3385
|
+
required: false
|
|
3386
|
+
},
|
|
3387
|
+
bundleId: {
|
|
3388
|
+
type: "string",
|
|
3389
|
+
required: false
|
|
3390
|
+
},
|
|
3391
|
+
msgId: {
|
|
3392
|
+
type: "string",
|
|
3393
|
+
required: false
|
|
3394
|
+
}
|
|
3395
|
+
},
|
|
3396
|
+
indexes: {
|
|
3397
|
+
/** Base table: PK = USER#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3398
|
+
record: {
|
|
3399
|
+
pk: {
|
|
3400
|
+
field: "PK",
|
|
3401
|
+
composite: ["id"],
|
|
3402
|
+
template: "USER#ID#${id}"
|
|
3403
|
+
},
|
|
3404
|
+
sk: {
|
|
3405
|
+
field: "SK",
|
|
3406
|
+
composite: ["sk"],
|
|
3407
|
+
template: "${sk}"
|
|
3408
|
+
}
|
|
3409
|
+
},
|
|
3410
|
+
/**
|
|
3411
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.
|
|
3412
|
+
* Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.
|
|
3413
|
+
* SK is derived via `gsi1skAttribute` — uses the resource's natural label when
|
|
3414
|
+
* extractable (string `name`/`title` via introspection), else `<lastUpdated>#<id>`
|
|
3415
|
+
* (DR-004). `casing: "none"` preserves the normalized label and ISO-8601 `T`/`Z`.
|
|
3416
|
+
*/
|
|
3417
|
+
gsi1: {
|
|
3418
|
+
index: "GSI1",
|
|
3419
|
+
pk: {
|
|
3420
|
+
field: "GSI1PK",
|
|
3421
|
+
composite: ["gsi1Shard"],
|
|
3422
|
+
template: "TID#-#WID#-#RT#User#SHARD#${gsi1Shard}"
|
|
3423
|
+
},
|
|
3424
|
+
sk: {
|
|
3425
|
+
field: "GSI1SK",
|
|
3426
|
+
casing: "none",
|
|
3427
|
+
composite: ["gsi1sk"],
|
|
3428
|
+
template: "${gsi1sk}"
|
|
3429
|
+
}
|
|
3430
|
+
},
|
|
3431
|
+
/**
|
|
3432
|
+
* GSI2 — Cognito sub-lookup per ADR-011: resolves the UserEntity from a Cognito `sub` claim.
|
|
3433
|
+
* `condition` skips the index when `cognitoSub` is missing so legacy items without a sub are
|
|
3434
|
+
* not indexed.
|
|
3435
|
+
*/
|
|
3436
|
+
gsi2: {
|
|
3437
|
+
index: "GSI2",
|
|
3438
|
+
condition: (attrs) => typeof attrs.cognitoSub === "string" && attrs.cognitoSub.length > 0,
|
|
3439
|
+
pk: {
|
|
3440
|
+
field: "GSI2PK",
|
|
3441
|
+
casing: "none",
|
|
3442
|
+
composite: ["cognitoSub"],
|
|
3443
|
+
template: "USER#SUB#${cognitoSub}"
|
|
3444
|
+
},
|
|
3445
|
+
sk: {
|
|
3446
|
+
field: "GSI2SK",
|
|
3447
|
+
casing: "none",
|
|
3448
|
+
composite: [],
|
|
3449
|
+
template: "CURRENT"
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
});
|
|
3454
|
+
|
|
3455
|
+
// src/data/dynamo/entities/control/workspace-entity.ts
|
|
3456
|
+
var import_electrodb7 = require("electrodb");
|
|
3457
|
+
var WorkspaceEntity = new import_electrodb7.Entity({
|
|
3458
|
+
model: {
|
|
3459
|
+
entity: "workspace",
|
|
3460
|
+
service: "control",
|
|
3461
|
+
version: "01"
|
|
3462
|
+
},
|
|
3463
|
+
attributes: {
|
|
3464
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3465
|
+
sk: {
|
|
3466
|
+
type: "string",
|
|
3467
|
+
required: true,
|
|
3468
|
+
default: "CURRENT"
|
|
3469
|
+
},
|
|
3470
|
+
/** Tenant that contains this workspace (required). */
|
|
3471
|
+
tenantId: {
|
|
3472
|
+
type: "string",
|
|
3473
|
+
required: true
|
|
3474
|
+
},
|
|
3475
|
+
/** FHIR Resource.id; logical id in URL. */
|
|
3476
|
+
id: {
|
|
3477
|
+
type: "string",
|
|
3478
|
+
required: true
|
|
3479
|
+
},
|
|
3480
|
+
/** Full Workspace resource serialized as JSON string. */
|
|
3481
|
+
resource: {
|
|
3482
|
+
type: "string",
|
|
3483
|
+
required: true
|
|
3484
|
+
},
|
|
3485
|
+
/**
|
|
3486
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3487
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3488
|
+
*/
|
|
3489
|
+
summary: {
|
|
3490
|
+
type: "string",
|
|
3491
|
+
required: true
|
|
3492
|
+
},
|
|
3493
|
+
/** Version id (e.g. ULID). */
|
|
3494
|
+
vid: {
|
|
3495
|
+
type: "string",
|
|
3496
|
+
required: true
|
|
3497
|
+
},
|
|
3498
|
+
lastUpdated: {
|
|
3499
|
+
type: "string",
|
|
3500
|
+
required: true
|
|
3501
|
+
},
|
|
3502
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3503
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3504
|
+
gsi1sk: gsi1skAttribute,
|
|
3505
|
+
deleted: {
|
|
3506
|
+
type: "boolean",
|
|
3507
|
+
required: false
|
|
3508
|
+
},
|
|
3509
|
+
bundleId: {
|
|
3510
|
+
type: "string",
|
|
3511
|
+
required: false
|
|
3512
|
+
},
|
|
3513
|
+
msgId: {
|
|
3514
|
+
type: "string",
|
|
3515
|
+
required: false
|
|
3516
|
+
}
|
|
3517
|
+
},
|
|
3518
|
+
indexes: {
|
|
3519
|
+
/** Base table: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3520
|
+
record: {
|
|
3521
|
+
pk: {
|
|
3522
|
+
field: "PK",
|
|
3523
|
+
composite: ["tenantId", "id"],
|
|
3524
|
+
template: "TID#${tenantId}#WORKSPACE#ID#${id}"
|
|
3525
|
+
},
|
|
3526
|
+
sk: {
|
|
3527
|
+
field: "SK",
|
|
3528
|
+
composite: ["sk"],
|
|
3529
|
+
template: "${sk}"
|
|
3530
|
+
}
|
|
3531
|
+
},
|
|
3532
|
+
/**
|
|
3533
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the
|
|
3534
|
+
* four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.
|
|
3535
|
+
* SK is derived via `gsi1skAttribute` — `<normalizedName>#<id>` when the resource
|
|
3536
|
+
* carries a `name`, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves
|
|
3537
|
+
* the normalized label and ISO-8601 `T`/`Z`.
|
|
3538
|
+
*/
|
|
3539
|
+
gsi1: {
|
|
3540
|
+
index: "GSI1",
|
|
3541
|
+
pk: {
|
|
3542
|
+
field: "GSI1PK",
|
|
3543
|
+
composite: ["tenantId", "gsi1Shard"],
|
|
3544
|
+
template: "TID#${tenantId}#WID#-#RT#Workspace#SHARD#${gsi1Shard}"
|
|
3545
|
+
},
|
|
3546
|
+
sk: {
|
|
3547
|
+
field: "GSI1SK",
|
|
3548
|
+
casing: "none",
|
|
3549
|
+
composite: ["gsi1sk"],
|
|
3550
|
+
template: "${gsi1sk}"
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
});
|
|
3555
|
+
|
|
3556
|
+
// src/data/dynamo/dynamo-control-service.ts
|
|
3557
|
+
var controlPlaneEntities = {
|
|
3558
|
+
configuration: ConfigurationEntity,
|
|
3559
|
+
membership: MembershipEntity,
|
|
3560
|
+
role: RoleEntity,
|
|
3561
|
+
roleAssignment: RoleAssignmentEntity,
|
|
3562
|
+
tenant: TenantEntity,
|
|
3563
|
+
user: UserEntity,
|
|
3564
|
+
workspace: WorkspaceEntity
|
|
3565
|
+
};
|
|
3566
|
+
var controlPlaneService = new import_electrodb8.Service(controlPlaneEntities, {
|
|
3567
|
+
table: defaultTableName,
|
|
3568
|
+
client: dynamoClient
|
|
3569
|
+
});
|
|
3570
|
+
var DynamoControlService = {
|
|
3571
|
+
entities: controlPlaneService.entities
|
|
3572
|
+
};
|
|
3573
|
+
|
|
3574
|
+
// src/data/operations/control/membership/membership-create-operation.ts
|
|
3575
|
+
var import_types3 = require("@openhi/types");
|
|
3576
|
+
|
|
3577
|
+
// src/data/operations/control/roleassignment/roleassignment-create-operation.ts
|
|
3578
|
+
var import_types4 = require("@openhi/types");
|
|
3579
|
+
|
|
3580
|
+
// src/data/operations/control/tenant/tenant-create-operation.ts
|
|
3581
|
+
var import_types5 = require("@openhi/types");
|
|
3582
|
+
|
|
3583
|
+
// src/data/operations/control/workspace/workspace-create-operation.ts
|
|
3584
|
+
var import_types6 = require("@openhi/types");
|
|
3585
|
+
|
|
3586
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts
|
|
3587
|
+
var SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR = "SEED_DEMO_DATA_USER_POOL_ID";
|
|
3588
|
+
|
|
3589
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data-lambda.ts
|
|
3590
|
+
var HANDLER_NAME7 = "seed-demo-data.handler.js";
|
|
3591
|
+
function resolveHandlerEntry7(dirname) {
|
|
3592
|
+
const sameDir = import_node_path7.default.join(dirname, HANDLER_NAME7);
|
|
3593
|
+
if (import_node_fs7.default.existsSync(sameDir)) {
|
|
3594
|
+
return sameDir;
|
|
3595
|
+
}
|
|
3596
|
+
return import_node_path7.default.join(dirname, "..", "..", "..", "..", "lib", HANDLER_NAME7);
|
|
3597
|
+
}
|
|
3598
|
+
var SeedDemoDataLambda = class extends import_constructs11.Construct {
|
|
3599
|
+
constructor(scope, props) {
|
|
3600
|
+
super(scope, "seed-demo-data-lambda");
|
|
3601
|
+
this.lambda = new import_aws_lambda_nodejs7.NodejsFunction(this, "handler", {
|
|
3602
|
+
entry: resolveHandlerEntry7(__dirname),
|
|
3603
|
+
runtime: import_aws_lambda7.Runtime.NODEJS_LATEST,
|
|
3604
|
+
memorySize: 512,
|
|
3605
|
+
timeout: import_aws_cdk_lib12.Duration.minutes(2),
|
|
3606
|
+
environment: {
|
|
3607
|
+
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,
|
|
3608
|
+
[SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR]: props.userPool.userPoolId
|
|
3609
|
+
}
|
|
3610
|
+
});
|
|
3611
|
+
const roleReadKeys = Object.values(import_types8.PLATFORM_ROLE_IDS).map(rolePartitionKey);
|
|
3612
|
+
this.lambda.addToRolePolicy(
|
|
3613
|
+
new import_aws_iam3.PolicyStatement({
|
|
3614
|
+
effect: import_aws_iam3.Effect.ALLOW,
|
|
3615
|
+
actions: ["dynamodb:GetItem"],
|
|
3616
|
+
resources: [props.dataStoreTable.tableArn],
|
|
3617
|
+
conditions: {
|
|
3618
|
+
"ForAllValues:StringEquals": {
|
|
3619
|
+
"dynamodb:LeadingKeys": roleReadKeys
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
})
|
|
3623
|
+
);
|
|
3624
|
+
const writeKeys = [
|
|
3625
|
+
...demoBasePartitionKeys(),
|
|
3626
|
+
...demoDevUserPartitionKeys(DEV_USERS)
|
|
3627
|
+
];
|
|
3628
|
+
this.lambda.addToRolePolicy(
|
|
3629
|
+
new import_aws_iam3.PolicyStatement({
|
|
3630
|
+
effect: import_aws_iam3.Effect.ALLOW,
|
|
3631
|
+
actions: ["dynamodb:PutItem", "dynamodb:UpdateItem"],
|
|
3632
|
+
resources: [props.dataStoreTable.tableArn],
|
|
3633
|
+
conditions: {
|
|
3634
|
+
"ForAllValues:StringEquals": {
|
|
3635
|
+
"dynamodb:LeadingKeys": writeKeys
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
})
|
|
3639
|
+
);
|
|
3640
|
+
this.lambda.addToRolePolicy(
|
|
3641
|
+
new import_aws_iam3.PolicyStatement({
|
|
3642
|
+
effect: import_aws_iam3.Effect.ALLOW,
|
|
3643
|
+
actions: [
|
|
3644
|
+
"cognito-idp:AdminCreateUser",
|
|
3645
|
+
"cognito-idp:AdminGetUser",
|
|
3646
|
+
"cognito-idp:AdminSetUserPassword"
|
|
3647
|
+
],
|
|
3648
|
+
resources: [
|
|
3649
|
+
import_aws_cdk_lib12.Stack.of(this).formatArn({
|
|
3650
|
+
service: "cognito-idp",
|
|
3651
|
+
resource: "userpool",
|
|
3652
|
+
resourceName: props.userPool.userPoolId
|
|
3653
|
+
})
|
|
3654
|
+
]
|
|
3655
|
+
})
|
|
3656
|
+
);
|
|
3657
|
+
this.rule = new import_aws_events6.Rule(this, "rule", {
|
|
3658
|
+
eventBus: props.controlEventBus,
|
|
3659
|
+
eventPattern: {
|
|
3660
|
+
source: [import_workflows2.PlatformSystemDataSeededV1.source],
|
|
3661
|
+
detailType: [import_workflows2.PlatformSystemDataSeededV1.detailType]
|
|
3662
|
+
},
|
|
3663
|
+
targets: [
|
|
3664
|
+
new import_aws_events_targets2.LambdaFunction(this.lambda, {
|
|
3665
|
+
retryAttempts: 2,
|
|
3666
|
+
maxEventAge: import_aws_cdk_lib12.Duration.hours(2)
|
|
3667
|
+
})
|
|
3668
|
+
]
|
|
3669
|
+
});
|
|
3670
|
+
}
|
|
3671
|
+
};
|
|
3672
|
+
|
|
3673
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data-workflow.ts
|
|
3674
|
+
var import_constructs12 = require("constructs");
|
|
3675
|
+
var SeedDemoDataWorkflow = class extends import_constructs12.Construct {
|
|
3676
|
+
constructor(scope, props) {
|
|
3677
|
+
super(scope, "seed-demo-data-workflow");
|
|
3678
|
+
this.seedDemoData = new SeedDemoDataLambda(this, {
|
|
3679
|
+
controlEventBus: props.controlEventBus,
|
|
3680
|
+
dataStoreTable: props.dataStoreTable,
|
|
3681
|
+
userPool: props.userPool
|
|
3682
|
+
});
|
|
3683
|
+
WorkflowDedupTable.grantConsumerFromLookup(
|
|
3684
|
+
this,
|
|
3685
|
+
this.seedDemoData.lambda,
|
|
3686
|
+
SEED_DEMO_DATA_CONSUMER_NAME
|
|
3687
|
+
);
|
|
3688
|
+
}
|
|
3689
|
+
};
|
|
3690
|
+
|
|
3691
|
+
// src/workflows/control-plane/seed-system-data/events.ts
|
|
3692
|
+
var import_workflows4 = __toESM(require_lib2());
|
|
3693
|
+
var SEED_SYSTEM_DATA_CONSUMER_NAME = "seed-system-data";
|
|
3694
|
+
var SEED_SYSTEM_DATA_ACTOR_SYSTEM = "seed-system-data";
|
|
3695
|
+
var SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR = "CONTROL_EVENT_BUS_NAME";
|
|
3696
|
+
|
|
3697
|
+
// src/workflows/control-plane/seed-system-data/seed-system-data-lambda.ts
|
|
3698
|
+
var import_node_fs8 = __toESM(require("fs"));
|
|
3699
|
+
var import_node_path8 = __toESM(require("path"));
|
|
3700
|
+
var import_types9 = require("@openhi/types");
|
|
3701
|
+
var import_aws_cdk_lib13 = require("aws-cdk-lib");
|
|
3702
|
+
var import_aws_events7 = require("aws-cdk-lib/aws-events");
|
|
3703
|
+
var import_aws_events_targets3 = require("aws-cdk-lib/aws-events-targets");
|
|
3704
|
+
var import_aws_iam4 = require("aws-cdk-lib/aws-iam");
|
|
3705
|
+
var import_aws_lambda8 = require("aws-cdk-lib/aws-lambda");
|
|
3706
|
+
var import_aws_lambda_nodejs8 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
3707
|
+
var import_constructs13 = require("constructs");
|
|
3708
|
+
var HANDLER_NAME8 = "seed-system-data.handler.js";
|
|
3709
|
+
function resolveHandlerEntry8(dirname) {
|
|
3710
|
+
const sameDir = import_node_path8.default.join(dirname, HANDLER_NAME8);
|
|
3711
|
+
if (import_node_fs8.default.existsSync(sameDir)) {
|
|
3712
|
+
return sameDir;
|
|
3713
|
+
}
|
|
3714
|
+
return import_node_path8.default.join(dirname, "..", "..", "..", "..", "lib", HANDLER_NAME8);
|
|
3715
|
+
}
|
|
3716
|
+
var SeedSystemDataLambda = class extends import_constructs13.Construct {
|
|
3717
|
+
constructor(scope, props) {
|
|
3718
|
+
super(scope, "seed-system-data-lambda");
|
|
3719
|
+
this.lambda = new import_aws_lambda_nodejs8.NodejsFunction(this, "handler", {
|
|
3720
|
+
entry: resolveHandlerEntry8(__dirname),
|
|
3721
|
+
runtime: import_aws_lambda8.Runtime.NODEJS_LATEST,
|
|
3722
|
+
memorySize: 512,
|
|
3723
|
+
timeout: import_aws_cdk_lib13.Duration.minutes(1),
|
|
3724
|
+
environment: {
|
|
3725
|
+
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,
|
|
3726
|
+
[SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR]: props.controlEventBus.eventBusName
|
|
3727
|
+
}
|
|
3728
|
+
});
|
|
3729
|
+
const roleArns = Object.values(import_types9.PLATFORM_ROLE_IDS).map(
|
|
3730
|
+
(id) => `role#id#${id}`
|
|
3731
|
+
);
|
|
3732
|
+
this.lambda.addToRolePolicy(
|
|
3733
|
+
new import_aws_iam4.PolicyStatement({
|
|
3734
|
+
effect: import_aws_iam4.Effect.ALLOW,
|
|
3735
|
+
actions: ["dynamodb:PutItem", "dynamodb:UpdateItem"],
|
|
3736
|
+
resources: [props.dataStoreTable.tableArn],
|
|
3737
|
+
conditions: {
|
|
3738
|
+
"ForAllValues:StringEquals": {
|
|
3739
|
+
"dynamodb:LeadingKeys": roleArns
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
})
|
|
3743
|
+
);
|
|
3744
|
+
props.controlEventBus.grantPutEventsTo(this.lambda);
|
|
3745
|
+
const hostStackName = import_aws_cdk_lib13.Stack.of(this).stackName;
|
|
3746
|
+
this.rule = new import_aws_events7.Rule(this, "rule", {
|
|
3747
|
+
eventBus: props.controlEventBus,
|
|
3748
|
+
eventPattern: {
|
|
3749
|
+
source: [import_workflows4.PlatformDeploymentCompletedV1.source],
|
|
3750
|
+
detailType: [import_workflows4.PlatformDeploymentCompletedV1.detailType],
|
|
3751
|
+
detail: {
|
|
3752
|
+
payload: {
|
|
3753
|
+
stackName: [hostStackName]
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
},
|
|
3757
|
+
targets: [
|
|
3758
|
+
new import_aws_events_targets3.LambdaFunction(this.lambda, {
|
|
3759
|
+
retryAttempts: 2,
|
|
3760
|
+
maxEventAge: import_aws_cdk_lib13.Duration.hours(2)
|
|
3761
|
+
})
|
|
3762
|
+
]
|
|
3763
|
+
});
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3766
|
+
|
|
3767
|
+
// src/workflows/control-plane/seed-system-data/seed-system-data-workflow.ts
|
|
3768
|
+
var import_constructs14 = require("constructs");
|
|
3769
|
+
var SeedSystemDataWorkflow = class extends import_constructs14.Construct {
|
|
3770
|
+
constructor(scope, props) {
|
|
3771
|
+
super(scope, "seed-system-data-workflow");
|
|
3772
|
+
this.seedSystemData = new SeedSystemDataLambda(this, {
|
|
3773
|
+
controlEventBus: props.controlEventBus,
|
|
3774
|
+
dataStoreTable: props.dataStoreTable
|
|
3775
|
+
});
|
|
3776
|
+
WorkflowDedupTable.grantConsumerFromLookup(
|
|
3777
|
+
this,
|
|
3778
|
+
this.seedSystemData.lambda,
|
|
3779
|
+
SEED_SYSTEM_DATA_CONSUMER_NAME
|
|
3780
|
+
);
|
|
3781
|
+
}
|
|
3782
|
+
};
|
|
3783
|
+
|
|
3784
|
+
// src/services/open-hi-data-service.ts
|
|
3785
|
+
var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
1604
3786
|
constructor(ohEnv, props = {}) {
|
|
1605
3787
|
super(ohEnv, _OpenHiDataService.SERVICE_TYPE, props);
|
|
3788
|
+
/**
|
|
3789
|
+
* Cached control-event-bus lookup. `OpenHiGlobalService.controlEventBusFromConstruct`
|
|
3790
|
+
* registers a child `EventBus.fromEventBusName` construct with a
|
|
3791
|
+
* fixed id under the scope it is passed, so calling it twice on the
|
|
3792
|
+
* same `OpenHiDataService` instance collides. The cache mirrors the
|
|
3793
|
+
* `private controlEventBus()` pattern already used in
|
|
3794
|
+
* `OpenHiAuthService`. Use {@link controlEventBus} from this class
|
|
3795
|
+
* — never call the static lookup from inside `OpenHiDataService`.
|
|
3796
|
+
*/
|
|
3797
|
+
this._controlEventBus = null;
|
|
1606
3798
|
this.props = props;
|
|
1607
3799
|
this.dataStoreChangeStream = new kinesis.Stream(
|
|
1608
3800
|
this,
|
|
@@ -1637,6 +3829,53 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
|
1637
3829
|
branchHash: this.branchHash
|
|
1638
3830
|
}
|
|
1639
3831
|
);
|
|
3832
|
+
this.seedSystemDataWorkflow = this.createSeedSystemDataWorkflow();
|
|
3833
|
+
this.seedDemoDataWorkflow = this.createSeedDemoDataWorkflow();
|
|
3834
|
+
}
|
|
3835
|
+
/**
|
|
3836
|
+
* Returns the data store table by name. Use from other stacks (e.g. REST API Lambda) to obtain an ITable reference.
|
|
3837
|
+
*/
|
|
3838
|
+
static dynamoDbDataStoreFromConstruct(scope, id = "dynamo-db-data-store") {
|
|
3839
|
+
return import_aws_dynamodb3.Table.fromTableName(scope, id, getDynamoDbDataStoreTableName(scope));
|
|
3840
|
+
}
|
|
3841
|
+
get serviceType() {
|
|
3842
|
+
return _OpenHiDataService.SERVICE_TYPE;
|
|
3843
|
+
}
|
|
3844
|
+
/**
|
|
3845
|
+
* Lazily looks up the control event bus exactly once per
|
|
3846
|
+
* `OpenHiDataService` instance and caches the reference. Every
|
|
3847
|
+
* workflow that consumes the bus must read it through this method
|
|
3848
|
+
* — see {@link _controlEventBus} for the underlying collision risk.
|
|
3849
|
+
*/
|
|
3850
|
+
controlEventBus() {
|
|
3851
|
+
if (this._controlEventBus === null) {
|
|
3852
|
+
this._controlEventBus = OpenHiGlobalService.controlEventBusFromConstruct(this);
|
|
3853
|
+
}
|
|
3854
|
+
return this._controlEventBus;
|
|
3855
|
+
}
|
|
3856
|
+
/**
|
|
3857
|
+
* Creates the seed-system-data workflow. Override to customize.
|
|
3858
|
+
*/
|
|
3859
|
+
createSeedSystemDataWorkflow() {
|
|
3860
|
+
return new SeedSystemDataWorkflow(this, {
|
|
3861
|
+
controlEventBus: this.controlEventBus(),
|
|
3862
|
+
dataStoreTable: this.dataStore
|
|
3863
|
+
});
|
|
3864
|
+
}
|
|
3865
|
+
/**
|
|
3866
|
+
* Creates the seed-demo-data workflow — but only on non-prod
|
|
3867
|
+
* stages. Returns `undefined` on prod so the workflow literally
|
|
3868
|
+
* does not exist in prod stacks. Override to customize.
|
|
3869
|
+
*/
|
|
3870
|
+
createSeedDemoDataWorkflow() {
|
|
3871
|
+
if (this.ohEnv.ohStage.stageType === import_config4.OPEN_HI_STAGE.PROD) {
|
|
3872
|
+
return void 0;
|
|
3873
|
+
}
|
|
3874
|
+
return new SeedDemoDataWorkflow(this, {
|
|
3875
|
+
controlEventBus: this.controlEventBus(),
|
|
3876
|
+
dataStoreTable: this.dataStore,
|
|
3877
|
+
userPool: OpenHiAuthService.userPoolFromConstruct(this)
|
|
3878
|
+
});
|
|
1640
3879
|
}
|
|
1641
3880
|
/**
|
|
1642
3881
|
* Creates the single-table DynamoDB data store.
|
|
@@ -1645,7 +3884,7 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
|
1645
3884
|
createDataStore() {
|
|
1646
3885
|
return new DynamoDbDataStore(this, "dynamo-db-data-store", {
|
|
1647
3886
|
kinesisStream: this.dataStoreChangeStream,
|
|
1648
|
-
stream:
|
|
3887
|
+
stream: import_aws_dynamodb3.StreamViewType.NEW_AND_OLD_IMAGES
|
|
1649
3888
|
});
|
|
1650
3889
|
}
|
|
1651
3890
|
};
|
|
@@ -1678,29 +3917,29 @@ var buildProvisionDefaultWorkspaceRequestedDetail = (event) => {
|
|
|
1678
3917
|
};
|
|
1679
3918
|
|
|
1680
3919
|
// src/workflows/control-plane/user-onboarding/provision-default-workspace-lambda.ts
|
|
1681
|
-
var
|
|
1682
|
-
var
|
|
1683
|
-
var
|
|
1684
|
-
var
|
|
1685
|
-
var
|
|
1686
|
-
var
|
|
1687
|
-
var
|
|
1688
|
-
var
|
|
1689
|
-
var
|
|
1690
|
-
var
|
|
1691
|
-
function
|
|
1692
|
-
const sameDir =
|
|
1693
|
-
if (
|
|
3920
|
+
var import_node_fs9 = __toESM(require("fs"));
|
|
3921
|
+
var import_node_path9 = __toESM(require("path"));
|
|
3922
|
+
var import_aws_cdk_lib14 = require("aws-cdk-lib");
|
|
3923
|
+
var import_aws_events8 = require("aws-cdk-lib/aws-events");
|
|
3924
|
+
var import_aws_events_targets4 = require("aws-cdk-lib/aws-events-targets");
|
|
3925
|
+
var import_aws_iam5 = require("aws-cdk-lib/aws-iam");
|
|
3926
|
+
var import_aws_lambda9 = require("aws-cdk-lib/aws-lambda");
|
|
3927
|
+
var import_aws_lambda_nodejs9 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
3928
|
+
var import_constructs15 = require("constructs");
|
|
3929
|
+
var HANDLER_NAME9 = "provision-default-workspace.handler.js";
|
|
3930
|
+
function resolveHandlerEntry9(dirname) {
|
|
3931
|
+
const sameDir = import_node_path9.default.join(dirname, HANDLER_NAME9);
|
|
3932
|
+
if (import_node_fs9.default.existsSync(sameDir)) {
|
|
1694
3933
|
return sameDir;
|
|
1695
3934
|
}
|
|
1696
|
-
return
|
|
3935
|
+
return import_node_path9.default.join(dirname, "..", "..", "..", "..", "lib", HANDLER_NAME9);
|
|
1697
3936
|
}
|
|
1698
|
-
var ProvisionDefaultWorkspaceLambda = class extends
|
|
3937
|
+
var ProvisionDefaultWorkspaceLambda = class extends import_constructs15.Construct {
|
|
1699
3938
|
constructor(scope, props) {
|
|
1700
3939
|
super(scope, "provision-default-workspace-lambda");
|
|
1701
|
-
this.lambda = new
|
|
1702
|
-
entry:
|
|
1703
|
-
runtime:
|
|
3940
|
+
this.lambda = new import_aws_lambda_nodejs9.NodejsFunction(this, "handler", {
|
|
3941
|
+
entry: resolveHandlerEntry9(__dirname),
|
|
3942
|
+
runtime: import_aws_lambda9.Runtime.NODEJS_LATEST,
|
|
1704
3943
|
memorySize: 1024,
|
|
1705
3944
|
environment: {
|
|
1706
3945
|
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName
|
|
@@ -1712,22 +3951,22 @@ var ProvisionDefaultWorkspaceLambda = class extends import_constructs8.Construct
|
|
|
1712
3951
|
"dynamodb:UpdateItem"
|
|
1713
3952
|
);
|
|
1714
3953
|
this.lambda.addToRolePolicy(
|
|
1715
|
-
new
|
|
1716
|
-
effect:
|
|
3954
|
+
new import_aws_iam5.PolicyStatement({
|
|
3955
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
1717
3956
|
actions: ["dynamodb:Query"],
|
|
1718
3957
|
resources: [`${props.dataStoreTable.tableArn}/index/*`]
|
|
1719
3958
|
})
|
|
1720
3959
|
);
|
|
1721
|
-
this.rule = new
|
|
3960
|
+
this.rule = new import_aws_events8.Rule(this, "rule", {
|
|
1722
3961
|
eventBus: props.controlEventBus,
|
|
1723
3962
|
eventPattern: {
|
|
1724
3963
|
source: [USER_ONBOARDING_EVENT_SOURCE],
|
|
1725
3964
|
detailType: [PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE]
|
|
1726
3965
|
},
|
|
1727
3966
|
targets: [
|
|
1728
|
-
new
|
|
3967
|
+
new import_aws_events_targets4.LambdaFunction(this.lambda, {
|
|
1729
3968
|
retryAttempts: 2,
|
|
1730
|
-
maxEventAge:
|
|
3969
|
+
maxEventAge: import_aws_cdk_lib14.Duration.hours(2)
|
|
1731
3970
|
})
|
|
1732
3971
|
]
|
|
1733
3972
|
});
|
|
@@ -1735,8 +3974,8 @@ var ProvisionDefaultWorkspaceLambda = class extends import_constructs8.Construct
|
|
|
1735
3974
|
};
|
|
1736
3975
|
|
|
1737
3976
|
// src/workflows/control-plane/user-onboarding/user-onboarding-workflow.ts
|
|
1738
|
-
var
|
|
1739
|
-
var UserOnboardingWorkflow = class extends
|
|
3977
|
+
var import_constructs16 = require("constructs");
|
|
3978
|
+
var UserOnboardingWorkflow = class extends import_constructs16.Construct {
|
|
1740
3979
|
constructor(scope, props) {
|
|
1741
3980
|
super(scope, "user-onboarding-workflow");
|
|
1742
3981
|
this.provisionDefaultWorkspace = new ProvisionDefaultWorkspaceLambda(this, {
|
|
@@ -1951,8 +4190,8 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1951
4190
|
const dynamoActions = ["dynamodb:GetItem", "dynamodb:Query"];
|
|
1952
4191
|
dataStoreTable.grant(this.preTokenGenerationLambda, ...dynamoActions);
|
|
1953
4192
|
this.preTokenGenerationLambda.addToRolePolicy(
|
|
1954
|
-
new
|
|
1955
|
-
effect:
|
|
4193
|
+
new import_aws_iam6.PolicyStatement({
|
|
4194
|
+
effect: import_aws_iam6.Effect.ALLOW,
|
|
1956
4195
|
actions: [...dynamoActions],
|
|
1957
4196
|
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
1958
4197
|
})
|
|
@@ -1973,7 +4212,7 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1973
4212
|
*/
|
|
1974
4213
|
grantPostAuthenticationPermissions() {
|
|
1975
4214
|
this.postAuthenticationLambda.addToRolePolicy(
|
|
1976
|
-
new
|
|
4215
|
+
new import_aws_iam6.PolicyStatement({
|
|
1977
4216
|
actions: ["cognito-idp:AdminUserGlobalSignOut"],
|
|
1978
4217
|
resources: [
|
|
1979
4218
|
import_core2.Stack.of(this).formatArn({
|
|
@@ -2020,7 +4259,7 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
2020
4259
|
* via env vars to drive `InitiateAuth`.
|
|
2021
4260
|
*/
|
|
2022
4261
|
createFixtureSeederClient() {
|
|
2023
|
-
if (this.ohEnv.ohStage.stageType ===
|
|
4262
|
+
if (this.ohEnv.ohStage.stageType === import_config5.OPEN_HI_STAGE.PROD) {
|
|
2024
4263
|
return void 0;
|
|
2025
4264
|
}
|
|
2026
4265
|
const client = new CognitoFixtureSeederClient(this, {
|
|
@@ -2057,62 +4296,62 @@ _OpenHiAuthService.SERVICE_TYPE = "auth";
|
|
|
2057
4296
|
var OpenHiAuthService = _OpenHiAuthService;
|
|
2058
4297
|
|
|
2059
4298
|
// src/services/open-hi-rest-api-service.ts
|
|
2060
|
-
var
|
|
4299
|
+
var import_config6 = __toESM(require_lib());
|
|
2061
4300
|
var import_aws_apigatewayv22 = require("aws-cdk-lib/aws-apigatewayv2");
|
|
2062
4301
|
var import_aws_apigatewayv2_authorizers = require("aws-cdk-lib/aws-apigatewayv2-authorizers");
|
|
2063
4302
|
var import_aws_apigatewayv2_integrations = require("aws-cdk-lib/aws-apigatewayv2-integrations");
|
|
2064
|
-
var
|
|
4303
|
+
var import_aws_iam7 = require("aws-cdk-lib/aws-iam");
|
|
2065
4304
|
var import_aws_route533 = require("aws-cdk-lib/aws-route53");
|
|
2066
4305
|
var import_aws_route53_targets = require("aws-cdk-lib/aws-route53-targets");
|
|
2067
4306
|
var import_core3 = require("aws-cdk-lib/core");
|
|
2068
4307
|
|
|
2069
4308
|
// src/data/lambda/cors-options-lambda.ts
|
|
2070
|
-
var
|
|
2071
|
-
var
|
|
2072
|
-
var
|
|
2073
|
-
var
|
|
2074
|
-
var
|
|
2075
|
-
var
|
|
2076
|
-
function
|
|
2077
|
-
const sameDir =
|
|
2078
|
-
if (
|
|
4309
|
+
var import_node_fs10 = __toESM(require("fs"));
|
|
4310
|
+
var import_node_path10 = __toESM(require("path"));
|
|
4311
|
+
var import_aws_lambda10 = require("aws-cdk-lib/aws-lambda");
|
|
4312
|
+
var import_aws_lambda_nodejs10 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
4313
|
+
var import_constructs17 = require("constructs");
|
|
4314
|
+
var HANDLER_NAME10 = "cors-options-lambda.handler.js";
|
|
4315
|
+
function resolveHandlerEntry10(dirname) {
|
|
4316
|
+
const sameDir = import_node_path10.default.join(dirname, HANDLER_NAME10);
|
|
4317
|
+
if (import_node_fs10.default.existsSync(sameDir)) {
|
|
2079
4318
|
return sameDir;
|
|
2080
4319
|
}
|
|
2081
|
-
const fromLib =
|
|
4320
|
+
const fromLib = import_node_path10.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME10);
|
|
2082
4321
|
return fromLib;
|
|
2083
4322
|
}
|
|
2084
|
-
var CorsOptionsLambda = class extends
|
|
4323
|
+
var CorsOptionsLambda = class extends import_constructs17.Construct {
|
|
2085
4324
|
constructor(scope, id = "cors-options-lambda") {
|
|
2086
4325
|
super(scope, id);
|
|
2087
|
-
this.lambda = new
|
|
2088
|
-
entry:
|
|
2089
|
-
runtime:
|
|
4326
|
+
this.lambda = new import_aws_lambda_nodejs10.NodejsFunction(this, "handler", {
|
|
4327
|
+
entry: resolveHandlerEntry10(__dirname),
|
|
4328
|
+
runtime: import_aws_lambda10.Runtime.NODEJS_LATEST,
|
|
2090
4329
|
memorySize: 128
|
|
2091
4330
|
});
|
|
2092
4331
|
}
|
|
2093
4332
|
};
|
|
2094
4333
|
|
|
2095
4334
|
// src/data/lambda/rest-api-lambda.ts
|
|
2096
|
-
var
|
|
2097
|
-
var
|
|
2098
|
-
var
|
|
2099
|
-
var
|
|
2100
|
-
var
|
|
2101
|
-
var
|
|
2102
|
-
function
|
|
2103
|
-
const sameDir =
|
|
2104
|
-
if (
|
|
4335
|
+
var import_node_fs11 = __toESM(require("fs"));
|
|
4336
|
+
var import_node_path11 = __toESM(require("path"));
|
|
4337
|
+
var import_aws_lambda11 = require("aws-cdk-lib/aws-lambda");
|
|
4338
|
+
var import_aws_lambda_nodejs11 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
4339
|
+
var import_constructs18 = require("constructs");
|
|
4340
|
+
var HANDLER_NAME11 = "rest-api-lambda.handler.js";
|
|
4341
|
+
function resolveHandlerEntry11(dirname) {
|
|
4342
|
+
const sameDir = import_node_path11.default.join(dirname, HANDLER_NAME11);
|
|
4343
|
+
if (import_node_fs11.default.existsSync(sameDir)) {
|
|
2105
4344
|
return sameDir;
|
|
2106
4345
|
}
|
|
2107
|
-
const fromLib =
|
|
4346
|
+
const fromLib = import_node_path11.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME11);
|
|
2108
4347
|
return fromLib;
|
|
2109
4348
|
}
|
|
2110
|
-
var RestApiLambda = class extends
|
|
4349
|
+
var RestApiLambda = class extends import_constructs18.Construct {
|
|
2111
4350
|
constructor(scope, props) {
|
|
2112
4351
|
super(scope, "rest-api-lambda");
|
|
2113
|
-
this.lambda = new
|
|
2114
|
-
entry:
|
|
2115
|
-
runtime:
|
|
4352
|
+
this.lambda = new import_aws_lambda_nodejs11.NodejsFunction(this, "handler", {
|
|
4353
|
+
entry: resolveHandlerEntry11(__dirname),
|
|
4354
|
+
runtime: import_aws_lambda11.Runtime.NODEJS_LATEST,
|
|
2116
4355
|
memorySize: 1024,
|
|
2117
4356
|
environment: {
|
|
2118
4357
|
DYNAMO_TABLE_NAME: props.dynamoTableName,
|
|
@@ -2254,8 +4493,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2254
4493
|
postgresSchema
|
|
2255
4494
|
});
|
|
2256
4495
|
lambda.addToRolePolicy(
|
|
2257
|
-
new
|
|
2258
|
-
effect:
|
|
4496
|
+
new import_aws_iam7.PolicyStatement({
|
|
4497
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2259
4498
|
actions: [
|
|
2260
4499
|
"rds-data:ExecuteStatement",
|
|
2261
4500
|
"rds-data:BatchExecuteStatement"
|
|
@@ -2264,8 +4503,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2264
4503
|
})
|
|
2265
4504
|
);
|
|
2266
4505
|
lambda.addToRolePolicy(
|
|
2267
|
-
new
|
|
2268
|
-
effect:
|
|
4506
|
+
new import_aws_iam7.PolicyStatement({
|
|
4507
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2269
4508
|
actions: ["secretsmanager:GetSecretValue"],
|
|
2270
4509
|
resources: [postgresSecretArn]
|
|
2271
4510
|
})
|
|
@@ -2283,15 +4522,15 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2283
4522
|
];
|
|
2284
4523
|
dataStoreTable.grant(lambda, ...dynamoActions);
|
|
2285
4524
|
lambda.addToRolePolicy(
|
|
2286
|
-
new
|
|
2287
|
-
effect:
|
|
4525
|
+
new import_aws_iam7.PolicyStatement({
|
|
4526
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2288
4527
|
actions: [...dynamoActions],
|
|
2289
4528
|
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
2290
4529
|
})
|
|
2291
4530
|
);
|
|
2292
4531
|
lambda.addToRolePolicy(
|
|
2293
|
-
new
|
|
2294
|
-
effect:
|
|
4532
|
+
new import_aws_iam7.PolicyStatement({
|
|
4533
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2295
4534
|
actions: [
|
|
2296
4535
|
"ssm:GetParameter",
|
|
2297
4536
|
"ssm:GetParameters",
|
|
@@ -2350,7 +4589,7 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2350
4589
|
const userPool = OpenHiAuthService.userPoolFromConstruct(this);
|
|
2351
4590
|
const userPoolClient = OpenHiAuthService.userPoolClientFromConstruct(this);
|
|
2352
4591
|
const userPoolClients = [userPoolClient];
|
|
2353
|
-
if (this.ohEnv.ohStage.stageType !==
|
|
4592
|
+
if (this.ohEnv.ohStage.stageType !== import_config6.OPEN_HI_STAGE.PROD) {
|
|
2354
4593
|
userPoolClients.push(
|
|
2355
4594
|
OpenHiAuthService.fixtureSeederClientFromConstruct(this)
|
|
2356
4595
|
);
|
|
@@ -2440,6 +4679,10 @@ _OpenHiGraphqlService.SERVICE_TYPE = "graphql-api";
|
|
|
2440
4679
|
var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
2441
4680
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2442
4681
|
0 && (module.exports = {
|
|
4682
|
+
BRIDGED_STATUSES,
|
|
4683
|
+
CLOUDFORMATION_EVENT_SOURCE,
|
|
4684
|
+
CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE,
|
|
4685
|
+
CONTROL_EVENT_BUS_NAME_ENV_VAR,
|
|
2443
4686
|
ChildHostedZone,
|
|
2444
4687
|
CognitoFixtureSeederClient,
|
|
2445
4688
|
CognitoUserPool,
|
|
@@ -2450,11 +4693,22 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
|
2450
4693
|
DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES,
|
|
2451
4694
|
DATA_STORE_CHANGE_DETAIL_TYPE,
|
|
2452
4695
|
DATA_STORE_CHANGE_EVENT_SOURCE,
|
|
4696
|
+
DEMO_PERIOD,
|
|
4697
|
+
DEMO_TENANT_SPECS,
|
|
4698
|
+
DEMO_URN_SYSTEM,
|
|
4699
|
+
DEV_USERS,
|
|
2453
4700
|
DataEventBus,
|
|
2454
4701
|
DataStoreHistoricalArchive,
|
|
2455
4702
|
DataStorePostgresReplica,
|
|
2456
4703
|
DiscoverableStringParameter,
|
|
2457
4704
|
DynamoDbDataStore,
|
|
4705
|
+
OPENHI_REPO_TAG_KEY_ENV_VAR,
|
|
4706
|
+
OPENHI_RESOURCE_URN_SYSTEM,
|
|
4707
|
+
OPENHI_TAG_KEY_PREFIX_ENV_VAR,
|
|
4708
|
+
OPENHI_TAG_SUFFIX_BRANCH_NAME,
|
|
4709
|
+
OPENHI_TAG_SUFFIX_REPO_NAME,
|
|
4710
|
+
OPENHI_TAG_SUFFIX_SERVICE_TYPE,
|
|
4711
|
+
OPENHI_TAG_SUFFIX_STAGE_TYPE,
|
|
2458
4712
|
OpenHiApp,
|
|
2459
4713
|
OpenHiAuthService,
|
|
2460
4714
|
OpenHiDataService,
|
|
@@ -2465,10 +4719,17 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
|
2465
4719
|
OpenHiService,
|
|
2466
4720
|
OpenHiStage,
|
|
2467
4721
|
OpsEventBus,
|
|
4722
|
+
PLACEHOLDER_TENANT_ID,
|
|
4723
|
+
PLACEHOLDER_WORKSPACE_ID,
|
|
4724
|
+
PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM,
|
|
4725
|
+
PLATFORM_SCOPE_TENANT_ID,
|
|
2468
4726
|
POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
|
|
2469
4727
|
POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
|
|
2470
4728
|
POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
|
|
2471
4729
|
PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE,
|
|
4730
|
+
PlatformDeployBridge,
|
|
4731
|
+
PlatformDeployBridgeLambda,
|
|
4732
|
+
PlatformDeploymentCompletedV1,
|
|
2472
4733
|
PostAuthenticationLambda,
|
|
2473
4734
|
PostConfirmationLambda,
|
|
2474
4735
|
PreTokenGenerationLambda,
|
|
@@ -2478,13 +4739,39 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
|
2478
4739
|
RootHostedZone,
|
|
2479
4740
|
RootHttpApi,
|
|
2480
4741
|
RootWildcardCertificate,
|
|
4742
|
+
SEED_DEMO_DATA_CONSUMER_NAME,
|
|
4743
|
+
SEED_SYSTEM_DATA_ACTOR_SYSTEM,
|
|
4744
|
+
SEED_SYSTEM_DATA_CONSUMER_NAME,
|
|
4745
|
+
SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR,
|
|
2481
4746
|
STATIC_HOSTING_SERVICE_TYPE,
|
|
4747
|
+
SeedDemoDataLambda,
|
|
4748
|
+
SeedDemoDataWorkflow,
|
|
4749
|
+
SeedSystemDataLambda,
|
|
4750
|
+
SeedSystemDataWorkflow,
|
|
2482
4751
|
StaticHosting,
|
|
2483
4752
|
USER_ONBOARDING_EVENT_SOURCE,
|
|
2484
4753
|
UserOnboardingWorkflow,
|
|
4754
|
+
WorkflowDedupConsumerNameInvalidError,
|
|
4755
|
+
WorkflowDedupTable,
|
|
4756
|
+
WorkflowDedupTableDuplicateError,
|
|
2485
4757
|
buildFhirCurrentResourceChangeDetail,
|
|
2486
4758
|
buildProvisionDefaultWorkspaceRequestedDetail,
|
|
4759
|
+
demoBasePartitionKeys,
|
|
4760
|
+
demoDevUserPartitionKeys,
|
|
4761
|
+
demoMembershipId,
|
|
4762
|
+
demoMembershipPartitionKey,
|
|
4763
|
+
demoRoleAssignmentId,
|
|
4764
|
+
demoRoleAssignmentPartitionKey,
|
|
4765
|
+
demoRolesForUserInTenant,
|
|
4766
|
+
demoScenarioIdentifier,
|
|
4767
|
+
demoTenantPartitionKey,
|
|
4768
|
+
demoUserPartitionKey,
|
|
4769
|
+
demoWorkspacePartitionKey,
|
|
2487
4770
|
getDynamoDbDataStoreTableName,
|
|
2488
|
-
getPostgresReplicaSchemaName
|
|
4771
|
+
getPostgresReplicaSchemaName,
|
|
4772
|
+
getWorkflowDedupTableName,
|
|
4773
|
+
openHiTagKey,
|
|
4774
|
+
openhiResourceIdentifier,
|
|
4775
|
+
rolePartitionKey
|
|
2489
4776
|
});
|
|
2490
4777
|
//# sourceMappingURL=index.js.map
|