@openhi/constructs 0.0.105 → 0.0.106
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-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-QMIOLLAS.mjs +531 -0
- package/lib/chunk-QMIOLLAS.mjs.map +1 -0
- 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-DGep6C7w.d.mts +207 -0
- package/lib/events-DGep6C7w.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 +2400 -111
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +781 -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,1248 @@ 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.CONTROL_PLANE_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.CONTROL_PLANE_ROLE_CODE.SYSTEM_ADMIN
|
|
2714
|
+
)
|
|
2715
|
+
)
|
|
2716
|
+
);
|
|
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;
|
|
1603
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.CONTROL_PLANE_ROLE_IDS).map(
|
|
3612
|
+
rolePartitionKey
|
|
3613
|
+
);
|
|
3614
|
+
this.lambda.addToRolePolicy(
|
|
3615
|
+
new import_aws_iam3.PolicyStatement({
|
|
3616
|
+
effect: import_aws_iam3.Effect.ALLOW,
|
|
3617
|
+
actions: ["dynamodb:GetItem"],
|
|
3618
|
+
resources: [props.dataStoreTable.tableArn],
|
|
3619
|
+
conditions: {
|
|
3620
|
+
"ForAllValues:StringEquals": {
|
|
3621
|
+
"dynamodb:LeadingKeys": roleReadKeys
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
})
|
|
3625
|
+
);
|
|
3626
|
+
const writeKeys = [
|
|
3627
|
+
...demoBasePartitionKeys(),
|
|
3628
|
+
...demoDevUserPartitionKeys(DEV_USERS)
|
|
3629
|
+
];
|
|
3630
|
+
this.lambda.addToRolePolicy(
|
|
3631
|
+
new import_aws_iam3.PolicyStatement({
|
|
3632
|
+
effect: import_aws_iam3.Effect.ALLOW,
|
|
3633
|
+
actions: ["dynamodb:PutItem", "dynamodb:UpdateItem"],
|
|
3634
|
+
resources: [props.dataStoreTable.tableArn],
|
|
3635
|
+
conditions: {
|
|
3636
|
+
"ForAllValues:StringEquals": {
|
|
3637
|
+
"dynamodb:LeadingKeys": writeKeys
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
})
|
|
3641
|
+
);
|
|
3642
|
+
this.lambda.addToRolePolicy(
|
|
3643
|
+
new import_aws_iam3.PolicyStatement({
|
|
3644
|
+
effect: import_aws_iam3.Effect.ALLOW,
|
|
3645
|
+
actions: [
|
|
3646
|
+
"cognito-idp:AdminCreateUser",
|
|
3647
|
+
"cognito-idp:AdminGetUser",
|
|
3648
|
+
"cognito-idp:AdminSetUserPassword"
|
|
3649
|
+
],
|
|
3650
|
+
resources: [
|
|
3651
|
+
import_aws_cdk_lib12.Stack.of(this).formatArn({
|
|
3652
|
+
service: "cognito-idp",
|
|
3653
|
+
resource: "userpool",
|
|
3654
|
+
resourceName: props.userPool.userPoolId
|
|
3655
|
+
})
|
|
3656
|
+
]
|
|
3657
|
+
})
|
|
3658
|
+
);
|
|
3659
|
+
this.rule = new import_aws_events6.Rule(this, "rule", {
|
|
3660
|
+
eventBus: props.controlEventBus,
|
|
3661
|
+
eventPattern: {
|
|
3662
|
+
source: [import_workflows2.PlatformSystemDataSeededV1.source],
|
|
3663
|
+
detailType: [import_workflows2.PlatformSystemDataSeededV1.detailType]
|
|
3664
|
+
},
|
|
3665
|
+
targets: [
|
|
3666
|
+
new import_aws_events_targets2.LambdaFunction(this.lambda, {
|
|
3667
|
+
retryAttempts: 2,
|
|
3668
|
+
maxEventAge: import_aws_cdk_lib12.Duration.hours(2)
|
|
3669
|
+
})
|
|
3670
|
+
]
|
|
3671
|
+
});
|
|
3672
|
+
}
|
|
3673
|
+
};
|
|
3674
|
+
|
|
3675
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data-workflow.ts
|
|
3676
|
+
var import_constructs12 = require("constructs");
|
|
3677
|
+
var SeedDemoDataWorkflow = class extends import_constructs12.Construct {
|
|
3678
|
+
constructor(scope, props) {
|
|
3679
|
+
super(scope, "seed-demo-data-workflow");
|
|
3680
|
+
this.seedDemoData = new SeedDemoDataLambda(this, {
|
|
3681
|
+
controlEventBus: props.controlEventBus,
|
|
3682
|
+
dataStoreTable: props.dataStoreTable,
|
|
3683
|
+
userPool: props.userPool
|
|
3684
|
+
});
|
|
3685
|
+
WorkflowDedupTable.grantConsumerFromLookup(
|
|
3686
|
+
this,
|
|
3687
|
+
this.seedDemoData.lambda,
|
|
3688
|
+
SEED_DEMO_DATA_CONSUMER_NAME
|
|
3689
|
+
);
|
|
3690
|
+
}
|
|
3691
|
+
};
|
|
3692
|
+
|
|
3693
|
+
// src/workflows/control-plane/seed-system-data/events.ts
|
|
3694
|
+
var import_workflows4 = __toESM(require_lib2());
|
|
3695
|
+
var SEED_SYSTEM_DATA_CONSUMER_NAME = "seed-system-data";
|
|
3696
|
+
var SEED_SYSTEM_DATA_ACTOR_SYSTEM = "seed-system-data";
|
|
3697
|
+
var SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR = "CONTROL_EVENT_BUS_NAME";
|
|
3698
|
+
|
|
3699
|
+
// src/workflows/control-plane/seed-system-data/seed-system-data-lambda.ts
|
|
3700
|
+
var import_node_fs8 = __toESM(require("fs"));
|
|
3701
|
+
var import_node_path8 = __toESM(require("path"));
|
|
3702
|
+
var import_types9 = require("@openhi/types");
|
|
3703
|
+
var import_aws_cdk_lib13 = require("aws-cdk-lib");
|
|
3704
|
+
var import_aws_events7 = require("aws-cdk-lib/aws-events");
|
|
3705
|
+
var import_aws_events_targets3 = require("aws-cdk-lib/aws-events-targets");
|
|
3706
|
+
var import_aws_iam4 = require("aws-cdk-lib/aws-iam");
|
|
3707
|
+
var import_aws_lambda8 = require("aws-cdk-lib/aws-lambda");
|
|
3708
|
+
var import_aws_lambda_nodejs8 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
3709
|
+
var import_constructs13 = require("constructs");
|
|
3710
|
+
var HANDLER_NAME8 = "seed-system-data.handler.js";
|
|
3711
|
+
function resolveHandlerEntry8(dirname) {
|
|
3712
|
+
const sameDir = import_node_path8.default.join(dirname, HANDLER_NAME8);
|
|
3713
|
+
if (import_node_fs8.default.existsSync(sameDir)) {
|
|
3714
|
+
return sameDir;
|
|
3715
|
+
}
|
|
3716
|
+
return import_node_path8.default.join(dirname, "..", "..", "..", "..", "lib", HANDLER_NAME8);
|
|
3717
|
+
}
|
|
3718
|
+
var SeedSystemDataLambda = class extends import_constructs13.Construct {
|
|
3719
|
+
constructor(scope, props) {
|
|
3720
|
+
super(scope, "seed-system-data-lambda");
|
|
3721
|
+
this.lambda = new import_aws_lambda_nodejs8.NodejsFunction(this, "handler", {
|
|
3722
|
+
entry: resolveHandlerEntry8(__dirname),
|
|
3723
|
+
runtime: import_aws_lambda8.Runtime.NODEJS_LATEST,
|
|
3724
|
+
memorySize: 512,
|
|
3725
|
+
timeout: import_aws_cdk_lib13.Duration.minutes(1),
|
|
3726
|
+
environment: {
|
|
3727
|
+
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,
|
|
3728
|
+
[SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR]: props.controlEventBus.eventBusName
|
|
3729
|
+
}
|
|
3730
|
+
});
|
|
3731
|
+
const roleArns = Object.values(import_types9.CONTROL_PLANE_ROLE_IDS).map(
|
|
3732
|
+
(id) => `role#id#${id}`
|
|
3733
|
+
);
|
|
3734
|
+
this.lambda.addToRolePolicy(
|
|
3735
|
+
new import_aws_iam4.PolicyStatement({
|
|
3736
|
+
effect: import_aws_iam4.Effect.ALLOW,
|
|
3737
|
+
actions: ["dynamodb:PutItem", "dynamodb:UpdateItem"],
|
|
3738
|
+
resources: [props.dataStoreTable.tableArn],
|
|
3739
|
+
conditions: {
|
|
3740
|
+
"ForAllValues:StringEquals": {
|
|
3741
|
+
"dynamodb:LeadingKeys": roleArns
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
})
|
|
3745
|
+
);
|
|
3746
|
+
props.controlEventBus.grantPutEventsTo(this.lambda);
|
|
3747
|
+
const hostStackName = import_aws_cdk_lib13.Stack.of(this).stackName;
|
|
3748
|
+
this.rule = new import_aws_events7.Rule(this, "rule", {
|
|
3749
|
+
eventBus: props.controlEventBus,
|
|
3750
|
+
eventPattern: {
|
|
3751
|
+
source: [import_workflows4.PlatformDeploymentCompletedV1.source],
|
|
3752
|
+
detailType: [import_workflows4.PlatformDeploymentCompletedV1.detailType],
|
|
3753
|
+
detail: {
|
|
3754
|
+
payload: {
|
|
3755
|
+
stackName: [hostStackName]
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
},
|
|
3759
|
+
targets: [
|
|
3760
|
+
new import_aws_events_targets3.LambdaFunction(this.lambda, {
|
|
3761
|
+
retryAttempts: 2,
|
|
3762
|
+
maxEventAge: import_aws_cdk_lib13.Duration.hours(2)
|
|
3763
|
+
})
|
|
3764
|
+
]
|
|
3765
|
+
});
|
|
3766
|
+
}
|
|
3767
|
+
};
|
|
3768
|
+
|
|
3769
|
+
// src/workflows/control-plane/seed-system-data/seed-system-data-workflow.ts
|
|
3770
|
+
var import_constructs14 = require("constructs");
|
|
3771
|
+
var SeedSystemDataWorkflow = class extends import_constructs14.Construct {
|
|
3772
|
+
constructor(scope, props) {
|
|
3773
|
+
super(scope, "seed-system-data-workflow");
|
|
3774
|
+
this.seedSystemData = new SeedSystemDataLambda(this, {
|
|
3775
|
+
controlEventBus: props.controlEventBus,
|
|
3776
|
+
dataStoreTable: props.dataStoreTable
|
|
3777
|
+
});
|
|
3778
|
+
WorkflowDedupTable.grantConsumerFromLookup(
|
|
3779
|
+
this,
|
|
3780
|
+
this.seedSystemData.lambda,
|
|
3781
|
+
SEED_SYSTEM_DATA_CONSUMER_NAME
|
|
3782
|
+
);
|
|
3783
|
+
}
|
|
3784
|
+
};
|
|
3785
|
+
|
|
3786
|
+
// src/services/open-hi-data-service.ts
|
|
3787
|
+
var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
1604
3788
|
constructor(ohEnv, props = {}) {
|
|
1605
3789
|
super(ohEnv, _OpenHiDataService.SERVICE_TYPE, props);
|
|
3790
|
+
/**
|
|
3791
|
+
* Cached control-event-bus lookup. `OpenHiGlobalService.controlEventBusFromConstruct`
|
|
3792
|
+
* registers a child `EventBus.fromEventBusName` construct with a
|
|
3793
|
+
* fixed id under the scope it is passed, so calling it twice on the
|
|
3794
|
+
* same `OpenHiDataService` instance collides. The cache mirrors the
|
|
3795
|
+
* `private controlEventBus()` pattern already used in
|
|
3796
|
+
* `OpenHiAuthService`. Use {@link controlEventBus} from this class
|
|
3797
|
+
* — never call the static lookup from inside `OpenHiDataService`.
|
|
3798
|
+
*/
|
|
3799
|
+
this._controlEventBus = null;
|
|
1606
3800
|
this.props = props;
|
|
1607
3801
|
this.dataStoreChangeStream = new kinesis.Stream(
|
|
1608
3802
|
this,
|
|
@@ -1637,6 +3831,53 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
|
1637
3831
|
branchHash: this.branchHash
|
|
1638
3832
|
}
|
|
1639
3833
|
);
|
|
3834
|
+
this.seedSystemDataWorkflow = this.createSeedSystemDataWorkflow();
|
|
3835
|
+
this.seedDemoDataWorkflow = this.createSeedDemoDataWorkflow();
|
|
3836
|
+
}
|
|
3837
|
+
/**
|
|
3838
|
+
* Returns the data store table by name. Use from other stacks (e.g. REST API Lambda) to obtain an ITable reference.
|
|
3839
|
+
*/
|
|
3840
|
+
static dynamoDbDataStoreFromConstruct(scope, id = "dynamo-db-data-store") {
|
|
3841
|
+
return import_aws_dynamodb3.Table.fromTableName(scope, id, getDynamoDbDataStoreTableName(scope));
|
|
3842
|
+
}
|
|
3843
|
+
get serviceType() {
|
|
3844
|
+
return _OpenHiDataService.SERVICE_TYPE;
|
|
3845
|
+
}
|
|
3846
|
+
/**
|
|
3847
|
+
* Lazily looks up the control event bus exactly once per
|
|
3848
|
+
* `OpenHiDataService` instance and caches the reference. Every
|
|
3849
|
+
* workflow that consumes the bus must read it through this method
|
|
3850
|
+
* — see {@link _controlEventBus} for the underlying collision risk.
|
|
3851
|
+
*/
|
|
3852
|
+
controlEventBus() {
|
|
3853
|
+
if (this._controlEventBus === null) {
|
|
3854
|
+
this._controlEventBus = OpenHiGlobalService.controlEventBusFromConstruct(this);
|
|
3855
|
+
}
|
|
3856
|
+
return this._controlEventBus;
|
|
3857
|
+
}
|
|
3858
|
+
/**
|
|
3859
|
+
* Creates the seed-system-data workflow. Override to customize.
|
|
3860
|
+
*/
|
|
3861
|
+
createSeedSystemDataWorkflow() {
|
|
3862
|
+
return new SeedSystemDataWorkflow(this, {
|
|
3863
|
+
controlEventBus: this.controlEventBus(),
|
|
3864
|
+
dataStoreTable: this.dataStore
|
|
3865
|
+
});
|
|
3866
|
+
}
|
|
3867
|
+
/**
|
|
3868
|
+
* Creates the seed-demo-data workflow — but only on non-prod
|
|
3869
|
+
* stages. Returns `undefined` on prod so the workflow literally
|
|
3870
|
+
* does not exist in prod stacks. Override to customize.
|
|
3871
|
+
*/
|
|
3872
|
+
createSeedDemoDataWorkflow() {
|
|
3873
|
+
if (this.ohEnv.ohStage.stageType === import_config4.OPEN_HI_STAGE.PROD) {
|
|
3874
|
+
return void 0;
|
|
3875
|
+
}
|
|
3876
|
+
return new SeedDemoDataWorkflow(this, {
|
|
3877
|
+
controlEventBus: this.controlEventBus(),
|
|
3878
|
+
dataStoreTable: this.dataStore,
|
|
3879
|
+
userPool: OpenHiAuthService.userPoolFromConstruct(this)
|
|
3880
|
+
});
|
|
1640
3881
|
}
|
|
1641
3882
|
/**
|
|
1642
3883
|
* Creates the single-table DynamoDB data store.
|
|
@@ -1645,7 +3886,7 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
|
1645
3886
|
createDataStore() {
|
|
1646
3887
|
return new DynamoDbDataStore(this, "dynamo-db-data-store", {
|
|
1647
3888
|
kinesisStream: this.dataStoreChangeStream,
|
|
1648
|
-
stream:
|
|
3889
|
+
stream: import_aws_dynamodb3.StreamViewType.NEW_AND_OLD_IMAGES
|
|
1649
3890
|
});
|
|
1650
3891
|
}
|
|
1651
3892
|
};
|
|
@@ -1678,29 +3919,29 @@ var buildProvisionDefaultWorkspaceRequestedDetail = (event) => {
|
|
|
1678
3919
|
};
|
|
1679
3920
|
|
|
1680
3921
|
// 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 (
|
|
3922
|
+
var import_node_fs9 = __toESM(require("fs"));
|
|
3923
|
+
var import_node_path9 = __toESM(require("path"));
|
|
3924
|
+
var import_aws_cdk_lib14 = require("aws-cdk-lib");
|
|
3925
|
+
var import_aws_events8 = require("aws-cdk-lib/aws-events");
|
|
3926
|
+
var import_aws_events_targets4 = require("aws-cdk-lib/aws-events-targets");
|
|
3927
|
+
var import_aws_iam5 = require("aws-cdk-lib/aws-iam");
|
|
3928
|
+
var import_aws_lambda9 = require("aws-cdk-lib/aws-lambda");
|
|
3929
|
+
var import_aws_lambda_nodejs9 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
3930
|
+
var import_constructs15 = require("constructs");
|
|
3931
|
+
var HANDLER_NAME9 = "provision-default-workspace.handler.js";
|
|
3932
|
+
function resolveHandlerEntry9(dirname) {
|
|
3933
|
+
const sameDir = import_node_path9.default.join(dirname, HANDLER_NAME9);
|
|
3934
|
+
if (import_node_fs9.default.existsSync(sameDir)) {
|
|
1694
3935
|
return sameDir;
|
|
1695
3936
|
}
|
|
1696
|
-
return
|
|
3937
|
+
return import_node_path9.default.join(dirname, "..", "..", "..", "..", "lib", HANDLER_NAME9);
|
|
1697
3938
|
}
|
|
1698
|
-
var ProvisionDefaultWorkspaceLambda = class extends
|
|
3939
|
+
var ProvisionDefaultWorkspaceLambda = class extends import_constructs15.Construct {
|
|
1699
3940
|
constructor(scope, props) {
|
|
1700
3941
|
super(scope, "provision-default-workspace-lambda");
|
|
1701
|
-
this.lambda = new
|
|
1702
|
-
entry:
|
|
1703
|
-
runtime:
|
|
3942
|
+
this.lambda = new import_aws_lambda_nodejs9.NodejsFunction(this, "handler", {
|
|
3943
|
+
entry: resolveHandlerEntry9(__dirname),
|
|
3944
|
+
runtime: import_aws_lambda9.Runtime.NODEJS_LATEST,
|
|
1704
3945
|
memorySize: 1024,
|
|
1705
3946
|
environment: {
|
|
1706
3947
|
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName
|
|
@@ -1712,22 +3953,22 @@ var ProvisionDefaultWorkspaceLambda = class extends import_constructs8.Construct
|
|
|
1712
3953
|
"dynamodb:UpdateItem"
|
|
1713
3954
|
);
|
|
1714
3955
|
this.lambda.addToRolePolicy(
|
|
1715
|
-
new
|
|
1716
|
-
effect:
|
|
3956
|
+
new import_aws_iam5.PolicyStatement({
|
|
3957
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
1717
3958
|
actions: ["dynamodb:Query"],
|
|
1718
3959
|
resources: [`${props.dataStoreTable.tableArn}/index/*`]
|
|
1719
3960
|
})
|
|
1720
3961
|
);
|
|
1721
|
-
this.rule = new
|
|
3962
|
+
this.rule = new import_aws_events8.Rule(this, "rule", {
|
|
1722
3963
|
eventBus: props.controlEventBus,
|
|
1723
3964
|
eventPattern: {
|
|
1724
3965
|
source: [USER_ONBOARDING_EVENT_SOURCE],
|
|
1725
3966
|
detailType: [PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE]
|
|
1726
3967
|
},
|
|
1727
3968
|
targets: [
|
|
1728
|
-
new
|
|
3969
|
+
new import_aws_events_targets4.LambdaFunction(this.lambda, {
|
|
1729
3970
|
retryAttempts: 2,
|
|
1730
|
-
maxEventAge:
|
|
3971
|
+
maxEventAge: import_aws_cdk_lib14.Duration.hours(2)
|
|
1731
3972
|
})
|
|
1732
3973
|
]
|
|
1733
3974
|
});
|
|
@@ -1735,8 +3976,8 @@ var ProvisionDefaultWorkspaceLambda = class extends import_constructs8.Construct
|
|
|
1735
3976
|
};
|
|
1736
3977
|
|
|
1737
3978
|
// src/workflows/control-plane/user-onboarding/user-onboarding-workflow.ts
|
|
1738
|
-
var
|
|
1739
|
-
var UserOnboardingWorkflow = class extends
|
|
3979
|
+
var import_constructs16 = require("constructs");
|
|
3980
|
+
var UserOnboardingWorkflow = class extends import_constructs16.Construct {
|
|
1740
3981
|
constructor(scope, props) {
|
|
1741
3982
|
super(scope, "user-onboarding-workflow");
|
|
1742
3983
|
this.provisionDefaultWorkspace = new ProvisionDefaultWorkspaceLambda(this, {
|
|
@@ -1951,8 +4192,8 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1951
4192
|
const dynamoActions = ["dynamodb:GetItem", "dynamodb:Query"];
|
|
1952
4193
|
dataStoreTable.grant(this.preTokenGenerationLambda, ...dynamoActions);
|
|
1953
4194
|
this.preTokenGenerationLambda.addToRolePolicy(
|
|
1954
|
-
new
|
|
1955
|
-
effect:
|
|
4195
|
+
new import_aws_iam6.PolicyStatement({
|
|
4196
|
+
effect: import_aws_iam6.Effect.ALLOW,
|
|
1956
4197
|
actions: [...dynamoActions],
|
|
1957
4198
|
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
1958
4199
|
})
|
|
@@ -1973,7 +4214,7 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1973
4214
|
*/
|
|
1974
4215
|
grantPostAuthenticationPermissions() {
|
|
1975
4216
|
this.postAuthenticationLambda.addToRolePolicy(
|
|
1976
|
-
new
|
|
4217
|
+
new import_aws_iam6.PolicyStatement({
|
|
1977
4218
|
actions: ["cognito-idp:AdminUserGlobalSignOut"],
|
|
1978
4219
|
resources: [
|
|
1979
4220
|
import_core2.Stack.of(this).formatArn({
|
|
@@ -2020,7 +4261,7 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
2020
4261
|
* via env vars to drive `InitiateAuth`.
|
|
2021
4262
|
*/
|
|
2022
4263
|
createFixtureSeederClient() {
|
|
2023
|
-
if (this.ohEnv.ohStage.stageType ===
|
|
4264
|
+
if (this.ohEnv.ohStage.stageType === import_config5.OPEN_HI_STAGE.PROD) {
|
|
2024
4265
|
return void 0;
|
|
2025
4266
|
}
|
|
2026
4267
|
const client = new CognitoFixtureSeederClient(this, {
|
|
@@ -2057,62 +4298,62 @@ _OpenHiAuthService.SERVICE_TYPE = "auth";
|
|
|
2057
4298
|
var OpenHiAuthService = _OpenHiAuthService;
|
|
2058
4299
|
|
|
2059
4300
|
// src/services/open-hi-rest-api-service.ts
|
|
2060
|
-
var
|
|
4301
|
+
var import_config6 = __toESM(require_lib());
|
|
2061
4302
|
var import_aws_apigatewayv22 = require("aws-cdk-lib/aws-apigatewayv2");
|
|
2062
4303
|
var import_aws_apigatewayv2_authorizers = require("aws-cdk-lib/aws-apigatewayv2-authorizers");
|
|
2063
4304
|
var import_aws_apigatewayv2_integrations = require("aws-cdk-lib/aws-apigatewayv2-integrations");
|
|
2064
|
-
var
|
|
4305
|
+
var import_aws_iam7 = require("aws-cdk-lib/aws-iam");
|
|
2065
4306
|
var import_aws_route533 = require("aws-cdk-lib/aws-route53");
|
|
2066
4307
|
var import_aws_route53_targets = require("aws-cdk-lib/aws-route53-targets");
|
|
2067
4308
|
var import_core3 = require("aws-cdk-lib/core");
|
|
2068
4309
|
|
|
2069
4310
|
// 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 (
|
|
4311
|
+
var import_node_fs10 = __toESM(require("fs"));
|
|
4312
|
+
var import_node_path10 = __toESM(require("path"));
|
|
4313
|
+
var import_aws_lambda10 = require("aws-cdk-lib/aws-lambda");
|
|
4314
|
+
var import_aws_lambda_nodejs10 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
4315
|
+
var import_constructs17 = require("constructs");
|
|
4316
|
+
var HANDLER_NAME10 = "cors-options-lambda.handler.js";
|
|
4317
|
+
function resolveHandlerEntry10(dirname) {
|
|
4318
|
+
const sameDir = import_node_path10.default.join(dirname, HANDLER_NAME10);
|
|
4319
|
+
if (import_node_fs10.default.existsSync(sameDir)) {
|
|
2079
4320
|
return sameDir;
|
|
2080
4321
|
}
|
|
2081
|
-
const fromLib =
|
|
4322
|
+
const fromLib = import_node_path10.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME10);
|
|
2082
4323
|
return fromLib;
|
|
2083
4324
|
}
|
|
2084
|
-
var CorsOptionsLambda = class extends
|
|
4325
|
+
var CorsOptionsLambda = class extends import_constructs17.Construct {
|
|
2085
4326
|
constructor(scope, id = "cors-options-lambda") {
|
|
2086
4327
|
super(scope, id);
|
|
2087
|
-
this.lambda = new
|
|
2088
|
-
entry:
|
|
2089
|
-
runtime:
|
|
4328
|
+
this.lambda = new import_aws_lambda_nodejs10.NodejsFunction(this, "handler", {
|
|
4329
|
+
entry: resolveHandlerEntry10(__dirname),
|
|
4330
|
+
runtime: import_aws_lambda10.Runtime.NODEJS_LATEST,
|
|
2090
4331
|
memorySize: 128
|
|
2091
4332
|
});
|
|
2092
4333
|
}
|
|
2093
4334
|
};
|
|
2094
4335
|
|
|
2095
4336
|
// 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 (
|
|
4337
|
+
var import_node_fs11 = __toESM(require("fs"));
|
|
4338
|
+
var import_node_path11 = __toESM(require("path"));
|
|
4339
|
+
var import_aws_lambda11 = require("aws-cdk-lib/aws-lambda");
|
|
4340
|
+
var import_aws_lambda_nodejs11 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
4341
|
+
var import_constructs18 = require("constructs");
|
|
4342
|
+
var HANDLER_NAME11 = "rest-api-lambda.handler.js";
|
|
4343
|
+
function resolveHandlerEntry11(dirname) {
|
|
4344
|
+
const sameDir = import_node_path11.default.join(dirname, HANDLER_NAME11);
|
|
4345
|
+
if (import_node_fs11.default.existsSync(sameDir)) {
|
|
2105
4346
|
return sameDir;
|
|
2106
4347
|
}
|
|
2107
|
-
const fromLib =
|
|
4348
|
+
const fromLib = import_node_path11.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME11);
|
|
2108
4349
|
return fromLib;
|
|
2109
4350
|
}
|
|
2110
|
-
var RestApiLambda = class extends
|
|
4351
|
+
var RestApiLambda = class extends import_constructs18.Construct {
|
|
2111
4352
|
constructor(scope, props) {
|
|
2112
4353
|
super(scope, "rest-api-lambda");
|
|
2113
|
-
this.lambda = new
|
|
2114
|
-
entry:
|
|
2115
|
-
runtime:
|
|
4354
|
+
this.lambda = new import_aws_lambda_nodejs11.NodejsFunction(this, "handler", {
|
|
4355
|
+
entry: resolveHandlerEntry11(__dirname),
|
|
4356
|
+
runtime: import_aws_lambda11.Runtime.NODEJS_LATEST,
|
|
2116
4357
|
memorySize: 1024,
|
|
2117
4358
|
environment: {
|
|
2118
4359
|
DYNAMO_TABLE_NAME: props.dynamoTableName,
|
|
@@ -2254,8 +4495,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2254
4495
|
postgresSchema
|
|
2255
4496
|
});
|
|
2256
4497
|
lambda.addToRolePolicy(
|
|
2257
|
-
new
|
|
2258
|
-
effect:
|
|
4498
|
+
new import_aws_iam7.PolicyStatement({
|
|
4499
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2259
4500
|
actions: [
|
|
2260
4501
|
"rds-data:ExecuteStatement",
|
|
2261
4502
|
"rds-data:BatchExecuteStatement"
|
|
@@ -2264,8 +4505,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2264
4505
|
})
|
|
2265
4506
|
);
|
|
2266
4507
|
lambda.addToRolePolicy(
|
|
2267
|
-
new
|
|
2268
|
-
effect:
|
|
4508
|
+
new import_aws_iam7.PolicyStatement({
|
|
4509
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2269
4510
|
actions: ["secretsmanager:GetSecretValue"],
|
|
2270
4511
|
resources: [postgresSecretArn]
|
|
2271
4512
|
})
|
|
@@ -2283,15 +4524,15 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2283
4524
|
];
|
|
2284
4525
|
dataStoreTable.grant(lambda, ...dynamoActions);
|
|
2285
4526
|
lambda.addToRolePolicy(
|
|
2286
|
-
new
|
|
2287
|
-
effect:
|
|
4527
|
+
new import_aws_iam7.PolicyStatement({
|
|
4528
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2288
4529
|
actions: [...dynamoActions],
|
|
2289
4530
|
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
2290
4531
|
})
|
|
2291
4532
|
);
|
|
2292
4533
|
lambda.addToRolePolicy(
|
|
2293
|
-
new
|
|
2294
|
-
effect:
|
|
4534
|
+
new import_aws_iam7.PolicyStatement({
|
|
4535
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2295
4536
|
actions: [
|
|
2296
4537
|
"ssm:GetParameter",
|
|
2297
4538
|
"ssm:GetParameters",
|
|
@@ -2350,7 +4591,7 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2350
4591
|
const userPool = OpenHiAuthService.userPoolFromConstruct(this);
|
|
2351
4592
|
const userPoolClient = OpenHiAuthService.userPoolClientFromConstruct(this);
|
|
2352
4593
|
const userPoolClients = [userPoolClient];
|
|
2353
|
-
if (this.ohEnv.ohStage.stageType !==
|
|
4594
|
+
if (this.ohEnv.ohStage.stageType !== import_config6.OPEN_HI_STAGE.PROD) {
|
|
2354
4595
|
userPoolClients.push(
|
|
2355
4596
|
OpenHiAuthService.fixtureSeederClientFromConstruct(this)
|
|
2356
4597
|
);
|
|
@@ -2440,6 +4681,10 @@ _OpenHiGraphqlService.SERVICE_TYPE = "graphql-api";
|
|
|
2440
4681
|
var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
2441
4682
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2442
4683
|
0 && (module.exports = {
|
|
4684
|
+
BRIDGED_STATUSES,
|
|
4685
|
+
CLOUDFORMATION_EVENT_SOURCE,
|
|
4686
|
+
CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE,
|
|
4687
|
+
CONTROL_EVENT_BUS_NAME_ENV_VAR,
|
|
2443
4688
|
ChildHostedZone,
|
|
2444
4689
|
CognitoFixtureSeederClient,
|
|
2445
4690
|
CognitoUserPool,
|
|
@@ -2450,11 +4695,22 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
|
2450
4695
|
DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES,
|
|
2451
4696
|
DATA_STORE_CHANGE_DETAIL_TYPE,
|
|
2452
4697
|
DATA_STORE_CHANGE_EVENT_SOURCE,
|
|
4698
|
+
DEMO_PERIOD,
|
|
4699
|
+
DEMO_TENANT_SPECS,
|
|
4700
|
+
DEMO_URN_SYSTEM,
|
|
4701
|
+
DEV_USERS,
|
|
2453
4702
|
DataEventBus,
|
|
2454
4703
|
DataStoreHistoricalArchive,
|
|
2455
4704
|
DataStorePostgresReplica,
|
|
2456
4705
|
DiscoverableStringParameter,
|
|
2457
4706
|
DynamoDbDataStore,
|
|
4707
|
+
OPENHI_REPO_TAG_KEY_ENV_VAR,
|
|
4708
|
+
OPENHI_RESOURCE_URN_SYSTEM,
|
|
4709
|
+
OPENHI_TAG_KEY_PREFIX_ENV_VAR,
|
|
4710
|
+
OPENHI_TAG_SUFFIX_BRANCH_NAME,
|
|
4711
|
+
OPENHI_TAG_SUFFIX_REPO_NAME,
|
|
4712
|
+
OPENHI_TAG_SUFFIX_SERVICE_TYPE,
|
|
4713
|
+
OPENHI_TAG_SUFFIX_STAGE_TYPE,
|
|
2458
4714
|
OpenHiApp,
|
|
2459
4715
|
OpenHiAuthService,
|
|
2460
4716
|
OpenHiDataService,
|
|
@@ -2465,10 +4721,17 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
|
2465
4721
|
OpenHiService,
|
|
2466
4722
|
OpenHiStage,
|
|
2467
4723
|
OpsEventBus,
|
|
4724
|
+
PLACEHOLDER_TENANT_ID,
|
|
4725
|
+
PLACEHOLDER_WORKSPACE_ID,
|
|
4726
|
+
PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM,
|
|
4727
|
+
PLATFORM_SCOPE_TENANT_ID,
|
|
2468
4728
|
POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
|
|
2469
4729
|
POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
|
|
2470
4730
|
POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
|
|
2471
4731
|
PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE,
|
|
4732
|
+
PlatformDeployBridge,
|
|
4733
|
+
PlatformDeployBridgeLambda,
|
|
4734
|
+
PlatformDeploymentCompletedV1,
|
|
2472
4735
|
PostAuthenticationLambda,
|
|
2473
4736
|
PostConfirmationLambda,
|
|
2474
4737
|
PreTokenGenerationLambda,
|
|
@@ -2478,13 +4741,39 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
|
2478
4741
|
RootHostedZone,
|
|
2479
4742
|
RootHttpApi,
|
|
2480
4743
|
RootWildcardCertificate,
|
|
4744
|
+
SEED_DEMO_DATA_CONSUMER_NAME,
|
|
4745
|
+
SEED_SYSTEM_DATA_ACTOR_SYSTEM,
|
|
4746
|
+
SEED_SYSTEM_DATA_CONSUMER_NAME,
|
|
4747
|
+
SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR,
|
|
2481
4748
|
STATIC_HOSTING_SERVICE_TYPE,
|
|
4749
|
+
SeedDemoDataLambda,
|
|
4750
|
+
SeedDemoDataWorkflow,
|
|
4751
|
+
SeedSystemDataLambda,
|
|
4752
|
+
SeedSystemDataWorkflow,
|
|
2482
4753
|
StaticHosting,
|
|
2483
4754
|
USER_ONBOARDING_EVENT_SOURCE,
|
|
2484
4755
|
UserOnboardingWorkflow,
|
|
4756
|
+
WorkflowDedupConsumerNameInvalidError,
|
|
4757
|
+
WorkflowDedupTable,
|
|
4758
|
+
WorkflowDedupTableDuplicateError,
|
|
2485
4759
|
buildFhirCurrentResourceChangeDetail,
|
|
2486
4760
|
buildProvisionDefaultWorkspaceRequestedDetail,
|
|
4761
|
+
demoBasePartitionKeys,
|
|
4762
|
+
demoDevUserPartitionKeys,
|
|
4763
|
+
demoMembershipId,
|
|
4764
|
+
demoMembershipPartitionKey,
|
|
4765
|
+
demoRoleAssignmentId,
|
|
4766
|
+
demoRoleAssignmentPartitionKey,
|
|
4767
|
+
demoRolesForUserInTenant,
|
|
4768
|
+
demoScenarioIdentifier,
|
|
4769
|
+
demoTenantPartitionKey,
|
|
4770
|
+
demoUserPartitionKey,
|
|
4771
|
+
demoWorkspacePartitionKey,
|
|
2487
4772
|
getDynamoDbDataStoreTableName,
|
|
2488
|
-
getPostgresReplicaSchemaName
|
|
4773
|
+
getPostgresReplicaSchemaName,
|
|
4774
|
+
getWorkflowDedupTableName,
|
|
4775
|
+
openHiTagKey,
|
|
4776
|
+
openhiResourceIdentifier,
|
|
4777
|
+
rolePartitionKey
|
|
2489
4778
|
});
|
|
2490
4779
|
//# sourceMappingURL=index.js.map
|