@openhi/constructs 0.0.104 → 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/README.md +14 -0
- package/lib/chunk-2PM2NGXI.mjs +31 -0
- package/lib/chunk-2PM2NGXI.mjs.map +1 -0
- package/lib/chunk-AGF3RAAZ.mjs +20 -0
- package/lib/chunk-AGF3RAAZ.mjs.map +1 -0
- package/lib/chunk-AO3E22CS.mjs +108 -0
- package/lib/chunk-AO3E22CS.mjs.map +1 -0
- package/lib/chunk-CHPEQRXU.mjs +45 -0
- package/lib/chunk-CHPEQRXU.mjs.map +1 -0
- 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-MLTYFMSE.mjs → chunk-VYDIGFIX.mjs} +74 -29
- 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-CVA3_eEB.d.mts +23 -0
- package/lib/events-CVA3_eEB.d.ts +23 -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 +508 -29
- package/lib/index.d.ts +773 -30
- package/lib/index.js +2536 -105
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +899 -106
- 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.js +50 -904
- package/lib/post-confirmation.handler.js.map +1 -1
- package/lib/post-confirmation.handler.mjs +37 -112
- package/lib/post-confirmation.handler.mjs.map +1 -1
- package/lib/pre-token-generation.handler.js +135 -55
- package/lib/pre-token-generation.handler.js.map +1 -1
- package/lib/pre-token-generation.handler.mjs +25 -32
- package/lib/pre-token-generation.handler.mjs.map +1 -1
- package/lib/provision-default-workspace.handler.d.mts +13 -0
- package/lib/provision-default-workspace.handler.d.ts +13 -0
- package/lib/provision-default-workspace.handler.js +1172 -0
- package/lib/provision-default-workspace.handler.js.map +1 -0
- package/lib/provision-default-workspace.handler.mjs +175 -0
- package/lib/provision-default-workspace.handler.mjs.map +1 -0
- 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 +60 -587
- 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-MLTYFMSE.mjs.map +0 -1
- /package/lib/{chunk-3QS3WKRC.mjs.map → chunk-LZOMFHX3.mjs.map} +0 -0
package/lib/index.js
CHANGED
|
@@ -90,23 +90,637 @@ 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,
|
|
99
701
|
CognitoUserPoolClient: () => CognitoUserPoolClient,
|
|
100
702
|
CognitoUserPoolDomain: () => CognitoUserPoolDomain,
|
|
101
703
|
CognitoUserPoolKmsKey: () => CognitoUserPoolKmsKey,
|
|
704
|
+
ControlEventBus: () => ControlEventBus,
|
|
102
705
|
DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES: () => DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES,
|
|
103
706
|
DATA_STORE_CHANGE_DETAIL_TYPE: () => DATA_STORE_CHANGE_DETAIL_TYPE,
|
|
104
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,
|
|
105
712
|
DataEventBus: () => DataEventBus,
|
|
106
713
|
DataStoreHistoricalArchive: () => DataStoreHistoricalArchive,
|
|
107
714
|
DataStorePostgresReplica: () => DataStorePostgresReplica,
|
|
108
715
|
DiscoverableStringParameter: () => DiscoverableStringParameter,
|
|
109
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,
|
|
110
724
|
OpenHiApp: () => OpenHiApp,
|
|
111
725
|
OpenHiAuthService: () => OpenHiAuthService,
|
|
112
726
|
OpenHiDataService: () => OpenHiDataService,
|
|
@@ -117,22 +731,60 @@ __export(src_exports, {
|
|
|
117
731
|
OpenHiService: () => OpenHiService,
|
|
118
732
|
OpenHiStage: () => OpenHiStage,
|
|
119
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,
|
|
120
738
|
POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME: () => POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
|
|
121
739
|
POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME: () => POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
|
|
122
740
|
POSTGRES_REPLICA_SECRET_ARN_SSM_NAME: () => POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
|
|
741
|
+
PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE: () => PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE,
|
|
742
|
+
PlatformDeployBridge: () => PlatformDeployBridge,
|
|
743
|
+
PlatformDeployBridgeLambda: () => PlatformDeployBridgeLambda,
|
|
744
|
+
PlatformDeploymentCompletedV1: () => import_workflows4.PlatformDeploymentCompletedV1,
|
|
123
745
|
PostAuthenticationLambda: () => PostAuthenticationLambda,
|
|
124
746
|
PostConfirmationLambda: () => PostConfirmationLambda,
|
|
125
747
|
PreTokenGenerationLambda: () => PreTokenGenerationLambda,
|
|
748
|
+
ProvisionDefaultWorkspaceLambda: () => ProvisionDefaultWorkspaceLambda,
|
|
126
749
|
REST_API_BASE_URL_SSM_NAME: () => REST_API_BASE_URL_SSM_NAME,
|
|
127
750
|
RootGraphqlApi: () => RootGraphqlApi,
|
|
128
751
|
RootHostedZone: () => RootHostedZone,
|
|
129
752
|
RootHttpApi: () => RootHttpApi,
|
|
130
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,
|
|
131
758
|
STATIC_HOSTING_SERVICE_TYPE: () => STATIC_HOSTING_SERVICE_TYPE,
|
|
759
|
+
SeedDemoDataLambda: () => SeedDemoDataLambda,
|
|
760
|
+
SeedDemoDataWorkflow: () => SeedDemoDataWorkflow,
|
|
761
|
+
SeedSystemDataLambda: () => SeedSystemDataLambda,
|
|
762
|
+
SeedSystemDataWorkflow: () => SeedSystemDataWorkflow,
|
|
132
763
|
StaticHosting: () => StaticHosting,
|
|
764
|
+
USER_ONBOARDING_EVENT_SOURCE: () => USER_ONBOARDING_EVENT_SOURCE,
|
|
765
|
+
UserOnboardingWorkflow: () => UserOnboardingWorkflow,
|
|
766
|
+
WorkflowDedupConsumerNameInvalidError: () => WorkflowDedupConsumerNameInvalidError,
|
|
767
|
+
WorkflowDedupTable: () => WorkflowDedupTable,
|
|
768
|
+
WorkflowDedupTableDuplicateError: () => WorkflowDedupTableDuplicateError,
|
|
133
769
|
buildFhirCurrentResourceChangeDetail: () => buildFhirCurrentResourceChangeDetail,
|
|
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,
|
|
134
782
|
getDynamoDbDataStoreTableName: () => getDynamoDbDataStoreTableName,
|
|
135
|
-
getPostgresReplicaSchemaName: () => getPostgresReplicaSchemaName
|
|
783
|
+
getPostgresReplicaSchemaName: () => getPostgresReplicaSchemaName,
|
|
784
|
+
getWorkflowDedupTableName: () => getWorkflowDedupTableName,
|
|
785
|
+
openHiTagKey: () => openHiTagKey,
|
|
786
|
+
openhiResourceIdentifier: () => openhiResourceIdentifier,
|
|
787
|
+
rolePartitionKey: () => rolePartitionKey
|
|
136
788
|
});
|
|
137
789
|
module.exports = __toCommonJS(src_exports);
|
|
138
790
|
|
|
@@ -347,6 +999,11 @@ var import_utils = require("@codedrifters/utils");
|
|
|
347
999
|
var import_config3 = __toESM(require_lib());
|
|
348
1000
|
var import_aws_cdk_lib4 = require("aws-cdk-lib");
|
|
349
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}`;
|
|
350
1007
|
var OpenHiService = class extends import_aws_cdk_lib4.Stack {
|
|
351
1008
|
/**
|
|
352
1009
|
* Creates a new OpenHI service stack.
|
|
@@ -414,11 +1071,20 @@ var OpenHiService = class extends import_aws_cdk_lib4.Stack {
|
|
|
414
1071
|
`availability-zones:account=${account}:region=${region}`,
|
|
415
1072
|
[`${region}a`, `${region}b`, `${region}c`]
|
|
416
1073
|
);
|
|
417
|
-
import_aws_cdk_lib4.Tags.of(this).add(`${appName}:repo-name`, repoName.slice(0, 255));
|
|
418
|
-
import_aws_cdk_lib4.Tags.of(this).add(`${appName}:branch-name`, branchName.slice(0, 255));
|
|
419
|
-
import_aws_cdk_lib4.Tags.of(this).add(`${appName}:service-type`, id.slice(0, 255));
|
|
420
1074
|
import_aws_cdk_lib4.Tags.of(this).add(
|
|
421
|
-
|
|
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),
|
|
422
1088
|
ohEnv.ohStage.stageType.slice(0, 255)
|
|
423
1089
|
);
|
|
424
1090
|
}
|
|
@@ -761,14 +1427,13 @@ var import_aws_lambda2 = require("aws-cdk-lib/aws-lambda");
|
|
|
761
1427
|
var import_aws_lambda_nodejs2 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
762
1428
|
var import_constructs2 = require("constructs");
|
|
763
1429
|
var HANDLER_NAME2 = "post-confirmation.handler.js";
|
|
764
|
-
|
|
1430
|
+
var resolveHandlerEntry2 = (dirname) => {
|
|
765
1431
|
const sameDir = import_node_path2.default.join(dirname, HANDLER_NAME2);
|
|
766
1432
|
if (import_node_fs2.default.existsSync(sameDir)) {
|
|
767
1433
|
return sameDir;
|
|
768
1434
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
}
|
|
1435
|
+
return import_node_path2.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME2);
|
|
1436
|
+
};
|
|
772
1437
|
var PostConfirmationLambda = class extends import_constructs2.Construct {
|
|
773
1438
|
constructor(scope, props) {
|
|
774
1439
|
super(scope, "post-confirmation-lambda");
|
|
@@ -777,7 +1442,7 @@ var PostConfirmationLambda = class extends import_constructs2.Construct {
|
|
|
777
1442
|
runtime: import_aws_lambda2.Runtime.NODEJS_LATEST,
|
|
778
1443
|
memorySize: 1024,
|
|
779
1444
|
environment: {
|
|
780
|
-
|
|
1445
|
+
CONTROL_EVENT_BUS_NAME: props.controlEventBusName
|
|
781
1446
|
}
|
|
782
1447
|
});
|
|
783
1448
|
}
|
|
@@ -1105,6 +1770,204 @@ var DynamoDbDataStore = class extends import_aws_dynamodb.Table {
|
|
|
1105
1770
|
}
|
|
1106
1771
|
};
|
|
1107
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
|
+
|
|
1108
1971
|
// src/components/event-bridge/data-event-bus.ts
|
|
1109
1972
|
var import_aws_events = require("aws-cdk-lib/aws-events");
|
|
1110
1973
|
var DataEventBus = class _DataEventBus extends import_aws_events.EventBus {
|
|
@@ -1149,16 +2012,38 @@ var OpsEventBus = class _OpsEventBus extends import_aws_events2.EventBus {
|
|
|
1149
2012
|
}
|
|
1150
2013
|
};
|
|
1151
2014
|
|
|
2015
|
+
// src/components/event-bridge/control-event-bus.ts
|
|
2016
|
+
var import_aws_events3 = require("aws-cdk-lib/aws-events");
|
|
2017
|
+
var ControlEventBus = class _ControlEventBus extends import_aws_events3.EventBus {
|
|
2018
|
+
/*****************************************************************************
|
|
2019
|
+
*
|
|
2020
|
+
* Return a name for this EventBus based on the stack environment hash. This
|
|
2021
|
+
* name is common across all stacks since it's using the environment hash in
|
|
2022
|
+
* its name.
|
|
2023
|
+
*
|
|
2024
|
+
****************************************************************************/
|
|
2025
|
+
static getEventBusName(scope) {
|
|
2026
|
+
const stack = OpenHiService.of(scope);
|
|
2027
|
+
return `controlv1${stack.branchHash}`;
|
|
2028
|
+
}
|
|
2029
|
+
constructor(scope, props) {
|
|
2030
|
+
super(scope, "control-event-bus-v1", {
|
|
2031
|
+
...props,
|
|
2032
|
+
eventBusName: _ControlEventBus.getEventBusName(scope)
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
|
|
1152
2037
|
// src/components/postgres/data-store-postgres-replica.ts
|
|
1153
2038
|
var import_node_fs5 = __toESM(require("fs"));
|
|
1154
2039
|
var import_node_path5 = __toESM(require("path"));
|
|
1155
|
-
var
|
|
2040
|
+
var import_aws_cdk_lib9 = require("aws-cdk-lib");
|
|
1156
2041
|
var ec2 = __toESM(require("aws-cdk-lib/aws-ec2"));
|
|
1157
2042
|
var import_aws_lambda5 = require("aws-cdk-lib/aws-lambda");
|
|
1158
2043
|
var import_aws_lambda_event_sources = require("aws-cdk-lib/aws-lambda-event-sources");
|
|
1159
2044
|
var import_aws_lambda_nodejs5 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
1160
2045
|
var rds = __toESM(require("aws-cdk-lib/aws-rds"));
|
|
1161
|
-
var
|
|
2046
|
+
var import_constructs6 = require("constructs");
|
|
1162
2047
|
var HANDLER_NAME5 = "data-store-postgres-replication.handler.js";
|
|
1163
2048
|
var DEFAULT_DATABASE_NAME = "openhi";
|
|
1164
2049
|
var SCHEMA_NAME_PATTERN = /^[a-z_][a-z0-9_]{0,62}$/;
|
|
@@ -1181,7 +2066,7 @@ function getPostgresReplicaSchemaName(branchHash) {
|
|
|
1181
2066
|
}
|
|
1182
2067
|
return candidate;
|
|
1183
2068
|
}
|
|
1184
|
-
var DataStorePostgresReplica = class extends
|
|
2069
|
+
var DataStorePostgresReplica = class extends import_constructs6.Construct {
|
|
1185
2070
|
/**
|
|
1186
2071
|
* Resolve the cluster ARN published by an upstream {@link DataStorePostgresReplica}.
|
|
1187
2072
|
* Use from any stack that needs to grant `rds-data:ExecuteStatement` against
|
|
@@ -1218,7 +2103,7 @@ var DataStorePostgresReplica = class extends import_constructs5.Construct {
|
|
|
1218
2103
|
super(scope, id);
|
|
1219
2104
|
this.databaseName = props.databaseName ?? DEFAULT_DATABASE_NAME;
|
|
1220
2105
|
this.schemaName = getPostgresReplicaSchemaName(props.branchHash);
|
|
1221
|
-
const region =
|
|
2106
|
+
const region = import_aws_cdk_lib9.Stack.of(this).region;
|
|
1222
2107
|
this.vpc = props.vpc ?? new ec2.Vpc(this, "Vpc", {
|
|
1223
2108
|
availabilityZones: [`${region}a`, `${region}b`],
|
|
1224
2109
|
natGateways: 0,
|
|
@@ -1254,7 +2139,7 @@ var DataStorePostgresReplica = class extends import_constructs5.Construct {
|
|
|
1254
2139
|
entry: resolveHandlerEntry5(__dirname),
|
|
1255
2140
|
runtime: import_aws_lambda5.Runtime.NODEJS_LATEST,
|
|
1256
2141
|
memorySize: 512,
|
|
1257
|
-
timeout:
|
|
2142
|
+
timeout: import_aws_cdk_lib9.Duration.minutes(1),
|
|
1258
2143
|
vpc: this.vpc,
|
|
1259
2144
|
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
|
|
1260
2145
|
description: "Replicates DynamoDB current-resource changes into the Postgres `resources` JSONB table (ADR 2026-04-17-01).",
|
|
@@ -1281,7 +2166,7 @@ var DataStorePostgresReplica = class extends import_constructs5.Construct {
|
|
|
1281
2166
|
new import_aws_lambda_event_sources.KinesisEventSource(props.kinesisStream, {
|
|
1282
2167
|
startingPosition: import_aws_lambda5.StartingPosition.LATEST,
|
|
1283
2168
|
batchSize: 100,
|
|
1284
|
-
maxBatchingWindow:
|
|
2169
|
+
maxBatchingWindow: import_aws_cdk_lib9.Duration.seconds(5),
|
|
1285
2170
|
retryAttempts: 10,
|
|
1286
2171
|
bisectBatchOnError: true,
|
|
1287
2172
|
parallelizationFactor: 2,
|
|
@@ -1314,7 +2199,7 @@ var DataStorePostgresReplica = class extends import_constructs5.Construct {
|
|
|
1314
2199
|
};
|
|
1315
2200
|
|
|
1316
2201
|
// src/components/route-53/child-hosted-zone.ts
|
|
1317
|
-
var
|
|
2202
|
+
var import_aws_cdk_lib10 = require("aws-cdk-lib");
|
|
1318
2203
|
var import_aws_route53 = require("aws-cdk-lib/aws-route53");
|
|
1319
2204
|
var ChildHostedZone = class extends import_aws_route53.HostedZone {
|
|
1320
2205
|
constructor(scope, id, props) {
|
|
@@ -1323,7 +2208,7 @@ var ChildHostedZone = class extends import_aws_route53.HostedZone {
|
|
|
1323
2208
|
zone: props.parentHostedZone,
|
|
1324
2209
|
recordName: this.zoneName,
|
|
1325
2210
|
values: this.hostedZoneNameServers || [],
|
|
1326
|
-
ttl:
|
|
2211
|
+
ttl: import_aws_cdk_lib10.Duration.minutes(5)
|
|
1327
2212
|
});
|
|
1328
2213
|
}
|
|
1329
2214
|
};
|
|
@@ -1333,8 +2218,8 @@ var ChildHostedZone = class extends import_aws_route53.HostedZone {
|
|
|
1333
2218
|
ChildHostedZone.SSM_PARAM_NAME = "CHILDHOSTEDZONE";
|
|
1334
2219
|
|
|
1335
2220
|
// src/components/route-53/root-hosted-zone.ts
|
|
1336
|
-
var
|
|
1337
|
-
var RootHostedZone = class extends
|
|
2221
|
+
var import_constructs7 = require("constructs");
|
|
2222
|
+
var RootHostedZone = class extends import_constructs7.Construct {
|
|
1338
2223
|
};
|
|
1339
2224
|
|
|
1340
2225
|
// src/components/static-hosting/static-hosting.ts
|
|
@@ -1342,9 +2227,9 @@ var import_aws_cloudfront = require("aws-cdk-lib/aws-cloudfront");
|
|
|
1342
2227
|
var import_aws_cloudfront_origins = require("aws-cdk-lib/aws-cloudfront-origins");
|
|
1343
2228
|
var import_aws_s3 = require("aws-cdk-lib/aws-s3");
|
|
1344
2229
|
var import_core = require("aws-cdk-lib/core");
|
|
1345
|
-
var
|
|
2230
|
+
var import_constructs8 = require("constructs");
|
|
1346
2231
|
var STATIC_HOSTING_SERVICE_TYPE = "website";
|
|
1347
|
-
var _StaticHosting = class _StaticHosting extends
|
|
2232
|
+
var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
|
|
1348
2233
|
constructor(scope, id, props = {}) {
|
|
1349
2234
|
super(scope, id);
|
|
1350
2235
|
const stack = OpenHiService.of(scope);
|
|
@@ -1396,21 +2281,119 @@ _StaticHosting.SSM_PARAM_NAME_DISTRIBUTION_ARN = "STATIC_HOSTING_DISTRIBUTION_AR
|
|
|
1396
2281
|
var StaticHosting = _StaticHosting;
|
|
1397
2282
|
|
|
1398
2283
|
// src/services/open-hi-auth-service.ts
|
|
1399
|
-
var
|
|
2284
|
+
var import_config5 = __toESM(require_lib());
|
|
1400
2285
|
var import_aws_cognito5 = require("aws-cdk-lib/aws-cognito");
|
|
1401
|
-
var
|
|
2286
|
+
var import_aws_iam6 = require("aws-cdk-lib/aws-iam");
|
|
1402
2287
|
var import_aws_kms2 = require("aws-cdk-lib/aws-kms");
|
|
1403
2288
|
var import_core2 = require("aws-cdk-lib/core");
|
|
1404
2289
|
|
|
1405
2290
|
// src/services/open-hi-data-service.ts
|
|
1406
|
-
var
|
|
2291
|
+
var import_config4 = __toESM(require_lib());
|
|
2292
|
+
var import_aws_dynamodb3 = require("aws-cdk-lib/aws-dynamodb");
|
|
1407
2293
|
var kinesis = __toESM(require("aws-cdk-lib/aws-kinesis"));
|
|
1408
2294
|
|
|
1409
2295
|
// src/services/open-hi-global-service.ts
|
|
1410
2296
|
var import_aws_certificatemanager2 = require("aws-cdk-lib/aws-certificatemanager");
|
|
1411
|
-
var
|
|
2297
|
+
var import_aws_events5 = require("aws-cdk-lib/aws-events");
|
|
1412
2298
|
var import_aws_route532 = require("aws-cdk-lib/aws-route53");
|
|
1413
2299
|
var import_aws_ssm3 = require("aws-cdk-lib/aws-ssm");
|
|
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
|
|
1414
2397
|
var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
1415
2398
|
/**
|
|
1416
2399
|
* Returns an IHostedZone from the given attributes (no SSM). Use when the zone is imported from config.
|
|
@@ -1449,7 +2432,7 @@ var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
|
1449
2432
|
* Returns the data event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.
|
|
1450
2433
|
*/
|
|
1451
2434
|
static dataEventBusFromConstruct(scope) {
|
|
1452
|
-
return
|
|
2435
|
+
return import_aws_events5.EventBus.fromEventBusName(
|
|
1453
2436
|
scope,
|
|
1454
2437
|
"data-event-bus",
|
|
1455
2438
|
DataEventBus.getEventBusName(scope)
|
|
@@ -1459,12 +2442,33 @@ var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
|
1459
2442
|
* Returns the ops event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.
|
|
1460
2443
|
*/
|
|
1461
2444
|
static opsEventBusFromConstruct(scope) {
|
|
1462
|
-
return
|
|
2445
|
+
return import_aws_events5.EventBus.fromEventBusName(
|
|
1463
2446
|
scope,
|
|
1464
2447
|
"ops-event-bus",
|
|
1465
2448
|
OpsEventBus.getEventBusName(scope)
|
|
1466
2449
|
);
|
|
1467
2450
|
}
|
|
2451
|
+
/**
|
|
2452
|
+
* Returns the control-plane event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.
|
|
2453
|
+
*/
|
|
2454
|
+
static controlEventBusFromConstruct(scope) {
|
|
2455
|
+
return import_aws_events5.EventBus.fromEventBusName(
|
|
2456
|
+
scope,
|
|
2457
|
+
"control-event-bus",
|
|
2458
|
+
ControlEventBus.getEventBusName(scope)
|
|
2459
|
+
);
|
|
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
|
+
}
|
|
1468
2472
|
get serviceType() {
|
|
1469
2473
|
return _OpenHiGlobalService.SERVICE_TYPE;
|
|
1470
2474
|
}
|
|
@@ -1477,6 +2481,9 @@ var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
|
1477
2481
|
this.rootWildcardCertificate = this.createRootWildcardCertificate();
|
|
1478
2482
|
this.dataEventBus = this.createDataEventBus();
|
|
1479
2483
|
this.opsEventBus = this.createOpsEventBus();
|
|
2484
|
+
this.controlEventBus = this.createControlEventBus();
|
|
2485
|
+
this.workflowDedupTable = this.createWorkflowDedupTable();
|
|
2486
|
+
this.platformDeployBridge = this.createPlatformDeployBridge();
|
|
1480
2487
|
}
|
|
1481
2488
|
/**
|
|
1482
2489
|
* Validates that config required for the Global stack is present.
|
|
@@ -1541,23 +2548,1255 @@ var _OpenHiGlobalService = class _OpenHiGlobalService extends OpenHiService {
|
|
|
1541
2548
|
createOpsEventBus() {
|
|
1542
2549
|
return new OpsEventBus(this);
|
|
1543
2550
|
}
|
|
2551
|
+
/**
|
|
2552
|
+
* Creates the control-plane event bus.
|
|
2553
|
+
* Override to customize.
|
|
2554
|
+
*/
|
|
2555
|
+
createControlEventBus() {
|
|
2556
|
+
return new ControlEventBus(this);
|
|
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
|
+
}
|
|
1544
2575
|
};
|
|
1545
2576
|
_OpenHiGlobalService.SERVICE_TYPE = "global";
|
|
1546
2577
|
var OpenHiGlobalService = _OpenHiGlobalService;
|
|
1547
2578
|
|
|
1548
|
-
// src/
|
|
1549
|
-
var
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
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
|
+
}
|
|
1555
2684
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
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
|
+
);
|
|
1558
2717
|
}
|
|
2718
|
+
return keys;
|
|
2719
|
+
};
|
|
2720
|
+
|
|
2721
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data-lambda.ts
|
|
2722
|
+
var import_node_fs7 = __toESM(require("fs"));
|
|
2723
|
+
var import_node_path7 = __toESM(require("path"));
|
|
2724
|
+
var import_types8 = require("@openhi/types");
|
|
2725
|
+
var import_aws_cdk_lib12 = require("aws-cdk-lib");
|
|
2726
|
+
var import_aws_events6 = require("aws-cdk-lib/aws-events");
|
|
2727
|
+
var import_aws_events_targets2 = require("aws-cdk-lib/aws-events-targets");
|
|
2728
|
+
var import_aws_iam3 = require("aws-cdk-lib/aws-iam");
|
|
2729
|
+
var import_aws_lambda7 = require("aws-cdk-lib/aws-lambda");
|
|
2730
|
+
var import_aws_lambda_nodejs7 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
2731
|
+
var import_constructs11 = require("constructs");
|
|
2732
|
+
|
|
2733
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts
|
|
2734
|
+
var import_node_crypto = require("crypto");
|
|
2735
|
+
var import_client_cognito_identity_provider = require("@aws-sdk/client-cognito-identity-provider");
|
|
2736
|
+
var import_client_dynamodb2 = require("@aws-sdk/client-dynamodb");
|
|
2737
|
+
var import_types7 = require("@openhi/types");
|
|
2738
|
+
var import_workflows3 = __toESM(require_lib2());
|
|
2739
|
+
|
|
2740
|
+
// src/data/dynamo/dynamo-control-service.ts
|
|
2741
|
+
var import_electrodb8 = require("electrodb");
|
|
2742
|
+
|
|
2743
|
+
// src/data/dynamo/dynamo-client.ts
|
|
2744
|
+
var import_client_dynamodb = require("@aws-sdk/client-dynamodb");
|
|
2745
|
+
var defaultTableName = process.env.DYNAMO_TABLE_NAME ?? "jesttesttable";
|
|
2746
|
+
var dynamoClient = new import_client_dynamodb.DynamoDBClient({
|
|
2747
|
+
...process.env.MOCK_DYNAMODB_ENDPOINT && {
|
|
2748
|
+
endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
|
|
2749
|
+
sslEnabled: false,
|
|
2750
|
+
region: "local"
|
|
2751
|
+
}
|
|
2752
|
+
});
|
|
2753
|
+
|
|
2754
|
+
// src/data/dynamo/entities/control/configuration-entity.ts
|
|
2755
|
+
var import_electrodb = require("electrodb");
|
|
2756
|
+
|
|
2757
|
+
// src/data/dynamo/entities/control/control-entity-common.ts
|
|
2758
|
+
var import_types2 = require("@openhi/types");
|
|
2759
|
+
|
|
2760
|
+
// src/data/dynamo/shard.ts
|
|
2761
|
+
var SHARD_COUNT = 4;
|
|
2762
|
+
function computeShard(id) {
|
|
2763
|
+
let hash = 2166136261;
|
|
2764
|
+
for (let i = 0; i < id.length; i++) {
|
|
2765
|
+
hash ^= id.charCodeAt(i);
|
|
2766
|
+
hash = Math.imul(hash, 16777619);
|
|
2767
|
+
}
|
|
2768
|
+
return (hash >>> 0) % SHARD_COUNT;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
// src/data/dynamo/entities/control/control-entity-common.ts
|
|
2772
|
+
var gsi1ShardAttribute = {
|
|
2773
|
+
type: "string",
|
|
2774
|
+
watch: ["id"],
|
|
2775
|
+
set: (_val, item) => {
|
|
2776
|
+
if (typeof item?.id !== "string" || item.id.length === 0) {
|
|
2777
|
+
return void 0;
|
|
2778
|
+
}
|
|
2779
|
+
return String(computeShard(item.id));
|
|
2780
|
+
}
|
|
2781
|
+
};
|
|
2782
|
+
var gsi1skAttribute = {
|
|
2783
|
+
type: "string",
|
|
2784
|
+
watch: ["resource", "lastUpdated", "id"],
|
|
2785
|
+
set: (_val, item) => {
|
|
2786
|
+
const id = typeof item?.id === "string" ? item.id : "";
|
|
2787
|
+
const lastUpdated = typeof item?.lastUpdated === "string" ? item.lastUpdated : "";
|
|
2788
|
+
const fallback = `${lastUpdated}#${id}`;
|
|
2789
|
+
if (typeof item?.resource !== "string" || item.resource.length === 0) {
|
|
2790
|
+
return fallback;
|
|
2791
|
+
}
|
|
2792
|
+
let parsed;
|
|
2793
|
+
try {
|
|
2794
|
+
parsed = JSON.parse(item.resource);
|
|
2795
|
+
} catch {
|
|
2796
|
+
return fallback;
|
|
2797
|
+
}
|
|
2798
|
+
if (!parsed || typeof parsed !== "object") return fallback;
|
|
2799
|
+
const resourceType = parsed.resourceType;
|
|
2800
|
+
if (typeof resourceType !== "string") return fallback;
|
|
2801
|
+
const label = (0, import_types2.extractLabel)(parsed);
|
|
2802
|
+
return label !== void 0 ? `${label}#${id}` : fallback;
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
|
|
2806
|
+
// src/data/dynamo/entities/control/configuration-entity.ts
|
|
2807
|
+
var ConfigurationEntity = new import_electrodb.Entity({
|
|
2808
|
+
model: {
|
|
2809
|
+
entity: "configuration",
|
|
2810
|
+
service: "control",
|
|
2811
|
+
version: "01"
|
|
2812
|
+
},
|
|
2813
|
+
attributes: {
|
|
2814
|
+
/** Sort key. "CURRENT" for current version; version history in S3. */
|
|
2815
|
+
sk: {
|
|
2816
|
+
type: "string",
|
|
2817
|
+
required: true,
|
|
2818
|
+
default: "CURRENT"
|
|
2819
|
+
},
|
|
2820
|
+
/** Tenant scope. Use "BASELINE" when the config is baseline default (no tenant). */
|
|
2821
|
+
tenantId: {
|
|
2822
|
+
type: "string",
|
|
2823
|
+
required: true,
|
|
2824
|
+
default: "BASELINE"
|
|
2825
|
+
},
|
|
2826
|
+
/** Workspace scope. Use "-" when absent. */
|
|
2827
|
+
workspaceId: {
|
|
2828
|
+
type: "string",
|
|
2829
|
+
required: true,
|
|
2830
|
+
default: "-"
|
|
2831
|
+
},
|
|
2832
|
+
/** User scope. Use "-" when absent. */
|
|
2833
|
+
userId: {
|
|
2834
|
+
type: "string",
|
|
2835
|
+
required: true,
|
|
2836
|
+
default: "-"
|
|
2837
|
+
},
|
|
2838
|
+
/** Role scope. Use "-" when absent. */
|
|
2839
|
+
roleId: {
|
|
2840
|
+
type: "string",
|
|
2841
|
+
required: true,
|
|
2842
|
+
default: "-"
|
|
2843
|
+
},
|
|
2844
|
+
/** Config type (category), e.g. endpoints, branding, display. */
|
|
2845
|
+
key: {
|
|
2846
|
+
type: "string",
|
|
2847
|
+
required: true
|
|
2848
|
+
},
|
|
2849
|
+
/** FHIR Resource.id; logical id in URL and for the Configuration resource. */
|
|
2850
|
+
id: {
|
|
2851
|
+
type: "string",
|
|
2852
|
+
required: true
|
|
2853
|
+
},
|
|
2854
|
+
/** Payload as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */
|
|
2855
|
+
resource: {
|
|
2856
|
+
type: "string",
|
|
2857
|
+
required: true
|
|
2858
|
+
},
|
|
2859
|
+
/**
|
|
2860
|
+
* Summary projection (key display fields as JSON string: id, key, status).
|
|
2861
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
2862
|
+
*/
|
|
2863
|
+
summary: {
|
|
2864
|
+
type: "string",
|
|
2865
|
+
required: true
|
|
2866
|
+
},
|
|
2867
|
+
/** Version id (e.g. ULID). Tracks current version; S3 history key. */
|
|
2868
|
+
vid: {
|
|
2869
|
+
type: "string",
|
|
2870
|
+
required: true
|
|
2871
|
+
},
|
|
2872
|
+
lastUpdated: {
|
|
2873
|
+
type: "string",
|
|
2874
|
+
required: true
|
|
2875
|
+
},
|
|
2876
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
2877
|
+
deleted: {
|
|
2878
|
+
type: "boolean",
|
|
2879
|
+
required: false
|
|
2880
|
+
},
|
|
2881
|
+
bundleId: {
|
|
2882
|
+
type: "string",
|
|
2883
|
+
required: false
|
|
2884
|
+
},
|
|
2885
|
+
msgId: {
|
|
2886
|
+
type: "string",
|
|
2887
|
+
required: false
|
|
2888
|
+
}
|
|
2889
|
+
},
|
|
2890
|
+
indexes: {
|
|
2891
|
+
/** Base table: PK, SK (data store key names). PK is built from tenantId, workspaceId, userId, roleId; SK is built from key and sk. Do not supply PK or SK from outside. */
|
|
2892
|
+
record: {
|
|
2893
|
+
pk: {
|
|
2894
|
+
field: "PK",
|
|
2895
|
+
composite: ["tenantId", "workspaceId", "userId", "roleId"],
|
|
2896
|
+
template: "CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}"
|
|
2897
|
+
},
|
|
2898
|
+
sk: {
|
|
2899
|
+
field: "SK",
|
|
2900
|
+
composite: ["key", "sk"],
|
|
2901
|
+
template: "KEY#${key}#SK#${sk}"
|
|
2902
|
+
}
|
|
2903
|
+
},
|
|
2904
|
+
/**
|
|
2905
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Configuration entries for a
|
|
2906
|
+
* (tenant, workspace) across the four shards. Use for "list configs scoped to this tenant"
|
|
2907
|
+
* (workspaceId = "-") or "list configs scoped to this workspace". Does not support
|
|
2908
|
+
* hierarchical resolution in one query; use base table GetItem in fallback order
|
|
2909
|
+
* (user → workspace → tenant → baseline) for that.
|
|
2910
|
+
* SK is `<key>#<id>` — Configuration's `key` is a required entity attribute (the
|
|
2911
|
+
* config category: endpoints, branding, display, …) and the natural sort/lookup
|
|
2912
|
+
* dimension. `casing: "none"` preserves the literal key value.
|
|
2913
|
+
*/
|
|
2914
|
+
gsi1: {
|
|
2915
|
+
index: "GSI1",
|
|
2916
|
+
pk: {
|
|
2917
|
+
field: "GSI1PK",
|
|
2918
|
+
composite: ["tenantId", "workspaceId", "gsi1Shard"],
|
|
2919
|
+
template: "TID#${tenantId}#WID#${workspaceId}#RT#Configuration#SHARD#${gsi1Shard}"
|
|
2920
|
+
},
|
|
2921
|
+
sk: {
|
|
2922
|
+
field: "GSI1SK",
|
|
2923
|
+
casing: "none",
|
|
2924
|
+
composite: ["key", "id"],
|
|
2925
|
+
template: "${key}#${id}"
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
});
|
|
2930
|
+
|
|
2931
|
+
// src/data/dynamo/entities/control/membership-entity.ts
|
|
2932
|
+
var import_electrodb2 = require("electrodb");
|
|
2933
|
+
var MembershipEntity = new import_electrodb2.Entity({
|
|
2934
|
+
model: {
|
|
2935
|
+
entity: "membership",
|
|
2936
|
+
service: "control",
|
|
2937
|
+
version: "01"
|
|
2938
|
+
},
|
|
2939
|
+
attributes: {
|
|
2940
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
2941
|
+
sk: {
|
|
2942
|
+
type: "string",
|
|
2943
|
+
required: true,
|
|
2944
|
+
default: "CURRENT"
|
|
2945
|
+
},
|
|
2946
|
+
/** Tenant in which the user has membership (required). */
|
|
2947
|
+
tenantId: {
|
|
2948
|
+
type: "string",
|
|
2949
|
+
required: true
|
|
2950
|
+
},
|
|
2951
|
+
/** FHIR Resource.id; membership id. */
|
|
2952
|
+
id: {
|
|
2953
|
+
type: "string",
|
|
2954
|
+
required: true
|
|
2955
|
+
},
|
|
2956
|
+
/** Full Membership resource serialized as JSON string. */
|
|
2957
|
+
resource: {
|
|
2958
|
+
type: "string",
|
|
2959
|
+
required: true
|
|
2960
|
+
},
|
|
2961
|
+
/**
|
|
2962
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
2963
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
2964
|
+
*/
|
|
2965
|
+
summary: {
|
|
2966
|
+
type: "string",
|
|
2967
|
+
required: true
|
|
2968
|
+
},
|
|
2969
|
+
/** Version id (e.g. ULID). */
|
|
2970
|
+
vid: {
|
|
2971
|
+
type: "string",
|
|
2972
|
+
required: true
|
|
2973
|
+
},
|
|
2974
|
+
lastUpdated: {
|
|
2975
|
+
type: "string",
|
|
2976
|
+
required: true
|
|
2977
|
+
},
|
|
2978
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
2979
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
2980
|
+
gsi1sk: gsi1skAttribute,
|
|
2981
|
+
deleted: {
|
|
2982
|
+
type: "boolean",
|
|
2983
|
+
required: false
|
|
2984
|
+
},
|
|
2985
|
+
bundleId: {
|
|
2986
|
+
type: "string",
|
|
2987
|
+
required: false
|
|
2988
|
+
},
|
|
2989
|
+
msgId: {
|
|
2990
|
+
type: "string",
|
|
2991
|
+
required: false
|
|
2992
|
+
}
|
|
2993
|
+
},
|
|
2994
|
+
indexes: {
|
|
2995
|
+
/** Base table: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
2996
|
+
record: {
|
|
2997
|
+
pk: {
|
|
2998
|
+
field: "PK",
|
|
2999
|
+
composite: ["tenantId", "id"],
|
|
3000
|
+
template: "TID#${tenantId}#MEMBERSHIP#ID#${id}"
|
|
3001
|
+
},
|
|
3002
|
+
sk: {
|
|
3003
|
+
field: "SK",
|
|
3004
|
+
composite: ["sk"],
|
|
3005
|
+
template: "${sk}"
|
|
3006
|
+
}
|
|
3007
|
+
},
|
|
3008
|
+
/**
|
|
3009
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the
|
|
3010
|
+
* four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.
|
|
3011
|
+
* SK is derived via `gsi1skAttribute` — uses the resource's natural label when
|
|
3012
|
+
* extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
|
|
3013
|
+
* normalized label and ISO-8601 `T`/`Z`.
|
|
3014
|
+
*/
|
|
3015
|
+
gsi1: {
|
|
3016
|
+
index: "GSI1",
|
|
3017
|
+
pk: {
|
|
3018
|
+
field: "GSI1PK",
|
|
3019
|
+
composite: ["tenantId", "gsi1Shard"],
|
|
3020
|
+
template: "TID#${tenantId}#WID#-#RT#Membership#SHARD#${gsi1Shard}"
|
|
3021
|
+
},
|
|
3022
|
+
sk: {
|
|
3023
|
+
field: "GSI1SK",
|
|
3024
|
+
casing: "none",
|
|
3025
|
+
composite: ["gsi1sk"],
|
|
3026
|
+
template: "${gsi1sk}"
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
});
|
|
3031
|
+
|
|
3032
|
+
// src/data/dynamo/entities/control/role-entity.ts
|
|
3033
|
+
var import_electrodb3 = require("electrodb");
|
|
3034
|
+
var RoleEntity = new import_electrodb3.Entity({
|
|
3035
|
+
model: {
|
|
3036
|
+
entity: "role",
|
|
3037
|
+
service: "control",
|
|
3038
|
+
version: "01"
|
|
3039
|
+
},
|
|
3040
|
+
attributes: {
|
|
3041
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3042
|
+
sk: {
|
|
3043
|
+
type: "string",
|
|
3044
|
+
required: true,
|
|
3045
|
+
default: "CURRENT"
|
|
3046
|
+
},
|
|
3047
|
+
/** FHIR Resource.id; role id. */
|
|
3048
|
+
id: {
|
|
3049
|
+
type: "string",
|
|
3050
|
+
required: true
|
|
3051
|
+
},
|
|
3052
|
+
/** Full Role resource serialized as JSON string. */
|
|
3053
|
+
resource: {
|
|
3054
|
+
type: "string",
|
|
3055
|
+
required: true
|
|
3056
|
+
},
|
|
3057
|
+
/**
|
|
3058
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3059
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3060
|
+
*/
|
|
3061
|
+
summary: {
|
|
3062
|
+
type: "string",
|
|
3063
|
+
required: true
|
|
3064
|
+
},
|
|
3065
|
+
/** Version id (e.g. ULID). */
|
|
3066
|
+
vid: {
|
|
3067
|
+
type: "string",
|
|
3068
|
+
required: true
|
|
3069
|
+
},
|
|
3070
|
+
lastUpdated: {
|
|
3071
|
+
type: "string",
|
|
3072
|
+
required: true
|
|
3073
|
+
},
|
|
3074
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3075
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3076
|
+
gsi1sk: gsi1skAttribute,
|
|
3077
|
+
deleted: {
|
|
3078
|
+
type: "boolean",
|
|
3079
|
+
required: false
|
|
3080
|
+
},
|
|
3081
|
+
bundleId: {
|
|
3082
|
+
type: "string",
|
|
3083
|
+
required: false
|
|
3084
|
+
},
|
|
3085
|
+
msgId: {
|
|
3086
|
+
type: "string",
|
|
3087
|
+
required: false
|
|
3088
|
+
}
|
|
3089
|
+
},
|
|
3090
|
+
indexes: {
|
|
3091
|
+
/** Base table: PK = ROLE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3092
|
+
record: {
|
|
3093
|
+
pk: {
|
|
3094
|
+
field: "PK",
|
|
3095
|
+
composite: ["id"],
|
|
3096
|
+
template: "ROLE#ID#${id}"
|
|
3097
|
+
},
|
|
3098
|
+
sk: {
|
|
3099
|
+
field: "SK",
|
|
3100
|
+
composite: ["sk"],
|
|
3101
|
+
template: "${sk}"
|
|
3102
|
+
}
|
|
3103
|
+
},
|
|
3104
|
+
/**
|
|
3105
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.
|
|
3106
|
+
* Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.
|
|
3107
|
+
* SK is derived via `gsi1skAttribute` — uses the resource's natural label when
|
|
3108
|
+
* extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
|
|
3109
|
+
* normalized label and ISO-8601 `T`/`Z`.
|
|
3110
|
+
*/
|
|
3111
|
+
gsi1: {
|
|
3112
|
+
index: "GSI1",
|
|
3113
|
+
pk: {
|
|
3114
|
+
field: "GSI1PK",
|
|
3115
|
+
composite: ["gsi1Shard"],
|
|
3116
|
+
template: "TID#-#WID#-#RT#Role#SHARD#${gsi1Shard}"
|
|
3117
|
+
},
|
|
3118
|
+
sk: {
|
|
3119
|
+
field: "GSI1SK",
|
|
3120
|
+
casing: "none",
|
|
3121
|
+
composite: ["gsi1sk"],
|
|
3122
|
+
template: "${gsi1sk}"
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
});
|
|
3127
|
+
|
|
3128
|
+
// src/data/dynamo/entities/control/roleassignment-entity.ts
|
|
3129
|
+
var import_electrodb4 = require("electrodb");
|
|
3130
|
+
var RoleAssignmentEntity = new import_electrodb4.Entity({
|
|
3131
|
+
model: {
|
|
3132
|
+
entity: "roleassignment",
|
|
3133
|
+
service: "control",
|
|
3134
|
+
version: "01"
|
|
3135
|
+
},
|
|
3136
|
+
attributes: {
|
|
3137
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3138
|
+
sk: {
|
|
3139
|
+
type: "string",
|
|
3140
|
+
required: true,
|
|
3141
|
+
default: "CURRENT"
|
|
3142
|
+
},
|
|
3143
|
+
/** Tenant in which the role assignment applies (required). */
|
|
3144
|
+
tenantId: {
|
|
3145
|
+
type: "string",
|
|
3146
|
+
required: true
|
|
3147
|
+
},
|
|
3148
|
+
/** FHIR Resource.id; role assignment id. */
|
|
3149
|
+
id: {
|
|
3150
|
+
type: "string",
|
|
3151
|
+
required: true
|
|
3152
|
+
},
|
|
3153
|
+
/** Full RoleAssignment resource serialized as JSON string. */
|
|
3154
|
+
resource: {
|
|
3155
|
+
type: "string",
|
|
3156
|
+
required: true
|
|
3157
|
+
},
|
|
3158
|
+
/**
|
|
3159
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3160
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3161
|
+
*/
|
|
3162
|
+
summary: {
|
|
3163
|
+
type: "string",
|
|
3164
|
+
required: true
|
|
3165
|
+
},
|
|
3166
|
+
/** Version id (e.g. ULID). */
|
|
3167
|
+
vid: {
|
|
3168
|
+
type: "string",
|
|
3169
|
+
required: true
|
|
3170
|
+
},
|
|
3171
|
+
lastUpdated: {
|
|
3172
|
+
type: "string",
|
|
3173
|
+
required: true
|
|
3174
|
+
},
|
|
3175
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3176
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3177
|
+
gsi1sk: gsi1skAttribute,
|
|
3178
|
+
deleted: {
|
|
3179
|
+
type: "boolean",
|
|
3180
|
+
required: false
|
|
3181
|
+
},
|
|
3182
|
+
bundleId: {
|
|
3183
|
+
type: "string",
|
|
3184
|
+
required: false
|
|
3185
|
+
},
|
|
3186
|
+
msgId: {
|
|
3187
|
+
type: "string",
|
|
3188
|
+
required: false
|
|
3189
|
+
}
|
|
3190
|
+
},
|
|
3191
|
+
indexes: {
|
|
3192
|
+
/** Base table: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3193
|
+
record: {
|
|
3194
|
+
pk: {
|
|
3195
|
+
field: "PK",
|
|
3196
|
+
composite: ["tenantId", "id"],
|
|
3197
|
+
template: "TID#${tenantId}#ROLEASSIGNMENT#ID#${id}"
|
|
3198
|
+
},
|
|
3199
|
+
sk: {
|
|
3200
|
+
field: "SK",
|
|
3201
|
+
composite: ["sk"],
|
|
3202
|
+
template: "${sk}"
|
|
3203
|
+
}
|
|
3204
|
+
},
|
|
3205
|
+
/**
|
|
3206
|
+
* GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the
|
|
3207
|
+
* four shards. Tenant-scoped only, so `WID#-` is a sentinel.
|
|
3208
|
+
* SK is derived via `gsi1skAttribute` — uses the resource's natural label when
|
|
3209
|
+
* extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
|
|
3210
|
+
* normalized label and ISO-8601 `T`/`Z`.
|
|
3211
|
+
*/
|
|
3212
|
+
gsi1: {
|
|
3213
|
+
index: "GSI1",
|
|
3214
|
+
pk: {
|
|
3215
|
+
field: "GSI1PK",
|
|
3216
|
+
composite: ["tenantId", "gsi1Shard"],
|
|
3217
|
+
template: "TID#${tenantId}#WID#-#RT#RoleAssignment#SHARD#${gsi1Shard}"
|
|
3218
|
+
},
|
|
3219
|
+
sk: {
|
|
3220
|
+
field: "GSI1SK",
|
|
3221
|
+
casing: "none",
|
|
3222
|
+
composite: ["gsi1sk"],
|
|
3223
|
+
template: "${gsi1sk}"
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
});
|
|
3228
|
+
|
|
3229
|
+
// src/data/dynamo/entities/control/tenant-entity.ts
|
|
3230
|
+
var import_electrodb5 = require("electrodb");
|
|
3231
|
+
var TenantEntity = new import_electrodb5.Entity({
|
|
3232
|
+
model: {
|
|
3233
|
+
entity: "tenant",
|
|
3234
|
+
service: "control",
|
|
3235
|
+
version: "01"
|
|
3236
|
+
},
|
|
3237
|
+
attributes: {
|
|
3238
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3239
|
+
sk: {
|
|
3240
|
+
type: "string",
|
|
3241
|
+
required: true,
|
|
3242
|
+
default: "CURRENT"
|
|
3243
|
+
},
|
|
3244
|
+
/** The tenant's own id (= resource id). Drives the partition key. */
|
|
3245
|
+
tenantId: {
|
|
3246
|
+
type: "string",
|
|
3247
|
+
required: true
|
|
3248
|
+
},
|
|
3249
|
+
/** FHIR Resource.id; logical id in URL. Equals tenantId. */
|
|
3250
|
+
id: {
|
|
3251
|
+
type: "string",
|
|
3252
|
+
required: true
|
|
3253
|
+
},
|
|
3254
|
+
/** Full Tenant resource serialized as JSON string. */
|
|
3255
|
+
resource: {
|
|
3256
|
+
type: "string",
|
|
3257
|
+
required: true
|
|
3258
|
+
},
|
|
3259
|
+
/**
|
|
3260
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3261
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3262
|
+
*/
|
|
3263
|
+
summary: {
|
|
3264
|
+
type: "string",
|
|
3265
|
+
required: true
|
|
3266
|
+
},
|
|
3267
|
+
/** Version id (e.g. ULID). */
|
|
3268
|
+
vid: {
|
|
3269
|
+
type: "string",
|
|
3270
|
+
required: true
|
|
3271
|
+
},
|
|
3272
|
+
lastUpdated: {
|
|
3273
|
+
type: "string",
|
|
3274
|
+
required: true
|
|
3275
|
+
},
|
|
3276
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3277
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3278
|
+
gsi1sk: gsi1skAttribute,
|
|
3279
|
+
deleted: {
|
|
3280
|
+
type: "boolean",
|
|
3281
|
+
required: false
|
|
3282
|
+
},
|
|
3283
|
+
bundleId: {
|
|
3284
|
+
type: "string",
|
|
3285
|
+
required: false
|
|
3286
|
+
},
|
|
3287
|
+
msgId: {
|
|
3288
|
+
type: "string",
|
|
3289
|
+
required: false
|
|
3290
|
+
}
|
|
3291
|
+
},
|
|
3292
|
+
indexes: {
|
|
3293
|
+
/** Base table: PK = TENANT#ID#<tenantId>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3294
|
+
record: {
|
|
3295
|
+
pk: {
|
|
3296
|
+
field: "PK",
|
|
3297
|
+
composite: ["tenantId"],
|
|
3298
|
+
template: "TENANT#ID#${tenantId}"
|
|
3299
|
+
},
|
|
3300
|
+
sk: {
|
|
3301
|
+
field: "SK",
|
|
3302
|
+
composite: ["sk"],
|
|
3303
|
+
template: "${sk}"
|
|
3304
|
+
}
|
|
3305
|
+
},
|
|
3306
|
+
/**
|
|
3307
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.
|
|
3308
|
+
* Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`
|
|
3309
|
+
* sentinels precede `RT#Tenant#SHARD#<n>`. SK is derived via `gsi1skAttribute` —
|
|
3310
|
+
* `<normalizedName>#<id>` when the resource carries a `name`, else `<lastUpdated>#<id>`
|
|
3311
|
+
* (DR-004). `casing: "none"` preserves the normalized label and ISO-8601 `T`/`Z`.
|
|
3312
|
+
*/
|
|
3313
|
+
gsi1: {
|
|
3314
|
+
index: "GSI1",
|
|
3315
|
+
pk: {
|
|
3316
|
+
field: "GSI1PK",
|
|
3317
|
+
composite: ["gsi1Shard"],
|
|
3318
|
+
template: "TID#-#WID#-#RT#Tenant#SHARD#${gsi1Shard}"
|
|
3319
|
+
},
|
|
3320
|
+
sk: {
|
|
3321
|
+
field: "GSI1SK",
|
|
3322
|
+
casing: "none",
|
|
3323
|
+
composite: ["gsi1sk"],
|
|
3324
|
+
template: "${gsi1sk}"
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
});
|
|
3329
|
+
|
|
3330
|
+
// src/data/dynamo/entities/control/user-entity.ts
|
|
3331
|
+
var import_electrodb6 = require("electrodb");
|
|
3332
|
+
var UserEntity = new import_electrodb6.Entity({
|
|
3333
|
+
model: {
|
|
3334
|
+
entity: "user",
|
|
3335
|
+
service: "control",
|
|
3336
|
+
version: "01"
|
|
3337
|
+
},
|
|
3338
|
+
attributes: {
|
|
3339
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3340
|
+
sk: {
|
|
3341
|
+
type: "string",
|
|
3342
|
+
required: true,
|
|
3343
|
+
default: "CURRENT"
|
|
3344
|
+
},
|
|
3345
|
+
/** FHIR Resource.id; platform user id (ohi_uid). */
|
|
3346
|
+
id: {
|
|
3347
|
+
type: "string",
|
|
3348
|
+
required: true
|
|
3349
|
+
},
|
|
3350
|
+
/** Full User resource serialized as JSON string. */
|
|
3351
|
+
resource: {
|
|
3352
|
+
type: "string",
|
|
3353
|
+
required: true
|
|
3354
|
+
},
|
|
3355
|
+
/**
|
|
3356
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3357
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3358
|
+
*/
|
|
3359
|
+
summary: {
|
|
3360
|
+
type: "string",
|
|
3361
|
+
required: true
|
|
3362
|
+
},
|
|
3363
|
+
/**
|
|
3364
|
+
* Immutable Cognito-issued `sub` claim. Drives GSI2 (sub-lookup). Optional until the
|
|
3365
|
+
* Post Confirmation Lambda (#770) lands; required thereafter.
|
|
3366
|
+
*/
|
|
3367
|
+
cognitoSub: {
|
|
3368
|
+
type: "string",
|
|
3369
|
+
required: false
|
|
3370
|
+
},
|
|
3371
|
+
/** Version id (e.g. ULID). */
|
|
3372
|
+
vid: {
|
|
3373
|
+
type: "string",
|
|
3374
|
+
required: true
|
|
3375
|
+
},
|
|
3376
|
+
lastUpdated: {
|
|
3377
|
+
type: "string",
|
|
3378
|
+
required: true
|
|
3379
|
+
},
|
|
3380
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3381
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3382
|
+
gsi1sk: gsi1skAttribute,
|
|
3383
|
+
deleted: {
|
|
3384
|
+
type: "boolean",
|
|
3385
|
+
required: false
|
|
3386
|
+
},
|
|
3387
|
+
bundleId: {
|
|
3388
|
+
type: "string",
|
|
3389
|
+
required: false
|
|
3390
|
+
},
|
|
3391
|
+
msgId: {
|
|
3392
|
+
type: "string",
|
|
3393
|
+
required: false
|
|
3394
|
+
}
|
|
3395
|
+
},
|
|
3396
|
+
indexes: {
|
|
3397
|
+
/** Base table: PK = USER#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3398
|
+
record: {
|
|
3399
|
+
pk: {
|
|
3400
|
+
field: "PK",
|
|
3401
|
+
composite: ["id"],
|
|
3402
|
+
template: "USER#ID#${id}"
|
|
3403
|
+
},
|
|
3404
|
+
sk: {
|
|
3405
|
+
field: "SK",
|
|
3406
|
+
composite: ["sk"],
|
|
3407
|
+
template: "${sk}"
|
|
3408
|
+
}
|
|
3409
|
+
},
|
|
3410
|
+
/**
|
|
3411
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.
|
|
3412
|
+
* Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.
|
|
3413
|
+
* SK is derived via `gsi1skAttribute` — uses the resource's natural label when
|
|
3414
|
+
* extractable (string `name`/`title` via introspection), else `<lastUpdated>#<id>`
|
|
3415
|
+
* (DR-004). `casing: "none"` preserves the normalized label and ISO-8601 `T`/`Z`.
|
|
3416
|
+
*/
|
|
3417
|
+
gsi1: {
|
|
3418
|
+
index: "GSI1",
|
|
3419
|
+
pk: {
|
|
3420
|
+
field: "GSI1PK",
|
|
3421
|
+
composite: ["gsi1Shard"],
|
|
3422
|
+
template: "TID#-#WID#-#RT#User#SHARD#${gsi1Shard}"
|
|
3423
|
+
},
|
|
3424
|
+
sk: {
|
|
3425
|
+
field: "GSI1SK",
|
|
3426
|
+
casing: "none",
|
|
3427
|
+
composite: ["gsi1sk"],
|
|
3428
|
+
template: "${gsi1sk}"
|
|
3429
|
+
}
|
|
3430
|
+
},
|
|
3431
|
+
/**
|
|
3432
|
+
* GSI2 — Cognito sub-lookup per ADR-011: resolves the UserEntity from a Cognito `sub` claim.
|
|
3433
|
+
* `condition` skips the index when `cognitoSub` is missing so legacy items without a sub are
|
|
3434
|
+
* not indexed.
|
|
3435
|
+
*/
|
|
3436
|
+
gsi2: {
|
|
3437
|
+
index: "GSI2",
|
|
3438
|
+
condition: (attrs) => typeof attrs.cognitoSub === "string" && attrs.cognitoSub.length > 0,
|
|
3439
|
+
pk: {
|
|
3440
|
+
field: "GSI2PK",
|
|
3441
|
+
casing: "none",
|
|
3442
|
+
composite: ["cognitoSub"],
|
|
3443
|
+
template: "USER#SUB#${cognitoSub}"
|
|
3444
|
+
},
|
|
3445
|
+
sk: {
|
|
3446
|
+
field: "GSI2SK",
|
|
3447
|
+
casing: "none",
|
|
3448
|
+
composite: [],
|
|
3449
|
+
template: "CURRENT"
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
});
|
|
3454
|
+
|
|
3455
|
+
// src/data/dynamo/entities/control/workspace-entity.ts
|
|
3456
|
+
var import_electrodb7 = require("electrodb");
|
|
3457
|
+
var WorkspaceEntity = new import_electrodb7.Entity({
|
|
3458
|
+
model: {
|
|
3459
|
+
entity: "workspace",
|
|
3460
|
+
service: "control",
|
|
3461
|
+
version: "01"
|
|
3462
|
+
},
|
|
3463
|
+
attributes: {
|
|
3464
|
+
/** Sort key sentinel. Always "CURRENT". */
|
|
3465
|
+
sk: {
|
|
3466
|
+
type: "string",
|
|
3467
|
+
required: true,
|
|
3468
|
+
default: "CURRENT"
|
|
3469
|
+
},
|
|
3470
|
+
/** Tenant that contains this workspace (required). */
|
|
3471
|
+
tenantId: {
|
|
3472
|
+
type: "string",
|
|
3473
|
+
required: true
|
|
3474
|
+
},
|
|
3475
|
+
/** FHIR Resource.id; logical id in URL. */
|
|
3476
|
+
id: {
|
|
3477
|
+
type: "string",
|
|
3478
|
+
required: true
|
|
3479
|
+
},
|
|
3480
|
+
/** Full Workspace resource serialized as JSON string. */
|
|
3481
|
+
resource: {
|
|
3482
|
+
type: "string",
|
|
3483
|
+
required: true
|
|
3484
|
+
},
|
|
3485
|
+
/**
|
|
3486
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
3487
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
3488
|
+
*/
|
|
3489
|
+
summary: {
|
|
3490
|
+
type: "string",
|
|
3491
|
+
required: true
|
|
3492
|
+
},
|
|
3493
|
+
/** Version id (e.g. ULID). */
|
|
3494
|
+
vid: {
|
|
3495
|
+
type: "string",
|
|
3496
|
+
required: true
|
|
3497
|
+
},
|
|
3498
|
+
lastUpdated: {
|
|
3499
|
+
type: "string",
|
|
3500
|
+
required: true
|
|
3501
|
+
},
|
|
3502
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
3503
|
+
/** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
|
|
3504
|
+
gsi1sk: gsi1skAttribute,
|
|
3505
|
+
deleted: {
|
|
3506
|
+
type: "boolean",
|
|
3507
|
+
required: false
|
|
3508
|
+
},
|
|
3509
|
+
bundleId: {
|
|
3510
|
+
type: "string",
|
|
3511
|
+
required: false
|
|
3512
|
+
},
|
|
3513
|
+
msgId: {
|
|
3514
|
+
type: "string",
|
|
3515
|
+
required: false
|
|
3516
|
+
}
|
|
3517
|
+
},
|
|
3518
|
+
indexes: {
|
|
3519
|
+
/** Base table: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
3520
|
+
record: {
|
|
3521
|
+
pk: {
|
|
3522
|
+
field: "PK",
|
|
3523
|
+
composite: ["tenantId", "id"],
|
|
3524
|
+
template: "TID#${tenantId}#WORKSPACE#ID#${id}"
|
|
3525
|
+
},
|
|
3526
|
+
sk: {
|
|
3527
|
+
field: "SK",
|
|
3528
|
+
composite: ["sk"],
|
|
3529
|
+
template: "${sk}"
|
|
3530
|
+
}
|
|
3531
|
+
},
|
|
3532
|
+
/**
|
|
3533
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the
|
|
3534
|
+
* four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.
|
|
3535
|
+
* SK is derived via `gsi1skAttribute` — `<normalizedName>#<id>` when the resource
|
|
3536
|
+
* carries a `name`, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves
|
|
3537
|
+
* the normalized label and ISO-8601 `T`/`Z`.
|
|
3538
|
+
*/
|
|
3539
|
+
gsi1: {
|
|
3540
|
+
index: "GSI1",
|
|
3541
|
+
pk: {
|
|
3542
|
+
field: "GSI1PK",
|
|
3543
|
+
composite: ["tenantId", "gsi1Shard"],
|
|
3544
|
+
template: "TID#${tenantId}#WID#-#RT#Workspace#SHARD#${gsi1Shard}"
|
|
3545
|
+
},
|
|
3546
|
+
sk: {
|
|
3547
|
+
field: "GSI1SK",
|
|
3548
|
+
casing: "none",
|
|
3549
|
+
composite: ["gsi1sk"],
|
|
3550
|
+
template: "${gsi1sk}"
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
});
|
|
3555
|
+
|
|
3556
|
+
// src/data/dynamo/dynamo-control-service.ts
|
|
3557
|
+
var controlPlaneEntities = {
|
|
3558
|
+
configuration: ConfigurationEntity,
|
|
3559
|
+
membership: MembershipEntity,
|
|
3560
|
+
role: RoleEntity,
|
|
3561
|
+
roleAssignment: RoleAssignmentEntity,
|
|
3562
|
+
tenant: TenantEntity,
|
|
3563
|
+
user: UserEntity,
|
|
3564
|
+
workspace: WorkspaceEntity
|
|
3565
|
+
};
|
|
3566
|
+
var controlPlaneService = new import_electrodb8.Service(controlPlaneEntities, {
|
|
3567
|
+
table: defaultTableName,
|
|
3568
|
+
client: dynamoClient
|
|
3569
|
+
});
|
|
3570
|
+
var DynamoControlService = {
|
|
3571
|
+
entities: controlPlaneService.entities
|
|
3572
|
+
};
|
|
3573
|
+
|
|
3574
|
+
// src/data/operations/control/membership/membership-create-operation.ts
|
|
3575
|
+
var import_types3 = require("@openhi/types");
|
|
3576
|
+
|
|
3577
|
+
// src/data/operations/control/roleassignment/roleassignment-create-operation.ts
|
|
3578
|
+
var import_types4 = require("@openhi/types");
|
|
3579
|
+
|
|
3580
|
+
// src/data/operations/control/tenant/tenant-create-operation.ts
|
|
3581
|
+
var import_types5 = require("@openhi/types");
|
|
3582
|
+
|
|
3583
|
+
// src/data/operations/control/workspace/workspace-create-operation.ts
|
|
3584
|
+
var import_types6 = require("@openhi/types");
|
|
3585
|
+
|
|
3586
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts
|
|
3587
|
+
var SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR = "SEED_DEMO_DATA_USER_POOL_ID";
|
|
3588
|
+
|
|
3589
|
+
// src/workflows/control-plane/seed-demo-data/seed-demo-data-lambda.ts
|
|
3590
|
+
var HANDLER_NAME7 = "seed-demo-data.handler.js";
|
|
3591
|
+
function resolveHandlerEntry7(dirname) {
|
|
3592
|
+
const sameDir = import_node_path7.default.join(dirname, HANDLER_NAME7);
|
|
3593
|
+
if (import_node_fs7.default.existsSync(sameDir)) {
|
|
3594
|
+
return sameDir;
|
|
3595
|
+
}
|
|
3596
|
+
return import_node_path7.default.join(dirname, "..", "..", "..", "..", "lib", HANDLER_NAME7);
|
|
3597
|
+
}
|
|
3598
|
+
var SeedDemoDataLambda = class extends import_constructs11.Construct {
|
|
3599
|
+
constructor(scope, props) {
|
|
3600
|
+
super(scope, "seed-demo-data-lambda");
|
|
3601
|
+
this.lambda = new import_aws_lambda_nodejs7.NodejsFunction(this, "handler", {
|
|
3602
|
+
entry: resolveHandlerEntry7(__dirname),
|
|
3603
|
+
runtime: import_aws_lambda7.Runtime.NODEJS_LATEST,
|
|
3604
|
+
memorySize: 512,
|
|
3605
|
+
timeout: import_aws_cdk_lib12.Duration.minutes(2),
|
|
3606
|
+
environment: {
|
|
3607
|
+
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,
|
|
3608
|
+
[SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR]: props.userPool.userPoolId
|
|
3609
|
+
}
|
|
3610
|
+
});
|
|
3611
|
+
const roleReadKeys = Object.values(import_types8.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 {
|
|
1559
3788
|
constructor(ohEnv, props = {}) {
|
|
1560
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;
|
|
1561
3800
|
this.props = props;
|
|
1562
3801
|
this.dataStoreChangeStream = new kinesis.Stream(
|
|
1563
3802
|
this,
|
|
@@ -1592,6 +3831,53 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
|
1592
3831
|
branchHash: this.branchHash
|
|
1593
3832
|
}
|
|
1594
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
|
+
});
|
|
1595
3881
|
}
|
|
1596
3882
|
/**
|
|
1597
3883
|
* Creates the single-table DynamoDB data store.
|
|
@@ -1600,13 +3886,107 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
|
1600
3886
|
createDataStore() {
|
|
1601
3887
|
return new DynamoDbDataStore(this, "dynamo-db-data-store", {
|
|
1602
3888
|
kinesisStream: this.dataStoreChangeStream,
|
|
1603
|
-
stream:
|
|
3889
|
+
stream: import_aws_dynamodb3.StreamViewType.NEW_AND_OLD_IMAGES
|
|
1604
3890
|
});
|
|
1605
3891
|
}
|
|
1606
3892
|
};
|
|
1607
3893
|
_OpenHiDataService.SERVICE_TYPE = "data";
|
|
1608
3894
|
var OpenHiDataService = _OpenHiDataService;
|
|
1609
3895
|
|
|
3896
|
+
// src/workflows/control-plane/user-onboarding/events.ts
|
|
3897
|
+
var USER_ONBOARDING_EVENT_SOURCE = "openhi.control.user-onboarding";
|
|
3898
|
+
var PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE = "ProvisionDefaultWorkspaceRequested";
|
|
3899
|
+
var buildProvisionDefaultWorkspaceRequestedDetail = (event) => {
|
|
3900
|
+
const attrs = event.request?.userAttributes ?? {};
|
|
3901
|
+
const cognitoSub = attrs.sub?.trim();
|
|
3902
|
+
if (!cognitoSub) {
|
|
3903
|
+
return void 0;
|
|
3904
|
+
}
|
|
3905
|
+
const email = attrs.email?.trim();
|
|
3906
|
+
const displayName = email || event.userName || cognitoSub;
|
|
3907
|
+
return {
|
|
3908
|
+
cognitoSub,
|
|
3909
|
+
...email ? { email } : {},
|
|
3910
|
+
displayName,
|
|
3911
|
+
trigger: {
|
|
3912
|
+
source: "cognito.post-confirmation",
|
|
3913
|
+
triggerSource: event.triggerSource,
|
|
3914
|
+
userPoolId: event.userPoolId,
|
|
3915
|
+
userName: event.userName,
|
|
3916
|
+
clientId: event.callerContext?.clientId
|
|
3917
|
+
}
|
|
3918
|
+
};
|
|
3919
|
+
};
|
|
3920
|
+
|
|
3921
|
+
// src/workflows/control-plane/user-onboarding/provision-default-workspace-lambda.ts
|
|
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)) {
|
|
3935
|
+
return sameDir;
|
|
3936
|
+
}
|
|
3937
|
+
return import_node_path9.default.join(dirname, "..", "..", "..", "..", "lib", HANDLER_NAME9);
|
|
3938
|
+
}
|
|
3939
|
+
var ProvisionDefaultWorkspaceLambda = class extends import_constructs15.Construct {
|
|
3940
|
+
constructor(scope, props) {
|
|
3941
|
+
super(scope, "provision-default-workspace-lambda");
|
|
3942
|
+
this.lambda = new import_aws_lambda_nodejs9.NodejsFunction(this, "handler", {
|
|
3943
|
+
entry: resolveHandlerEntry9(__dirname),
|
|
3944
|
+
runtime: import_aws_lambda9.Runtime.NODEJS_LATEST,
|
|
3945
|
+
memorySize: 1024,
|
|
3946
|
+
environment: {
|
|
3947
|
+
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName
|
|
3948
|
+
}
|
|
3949
|
+
});
|
|
3950
|
+
props.dataStoreTable.grant(
|
|
3951
|
+
this.lambda,
|
|
3952
|
+
"dynamodb:PutItem",
|
|
3953
|
+
"dynamodb:UpdateItem"
|
|
3954
|
+
);
|
|
3955
|
+
this.lambda.addToRolePolicy(
|
|
3956
|
+
new import_aws_iam5.PolicyStatement({
|
|
3957
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
3958
|
+
actions: ["dynamodb:Query"],
|
|
3959
|
+
resources: [`${props.dataStoreTable.tableArn}/index/*`]
|
|
3960
|
+
})
|
|
3961
|
+
);
|
|
3962
|
+
this.rule = new import_aws_events8.Rule(this, "rule", {
|
|
3963
|
+
eventBus: props.controlEventBus,
|
|
3964
|
+
eventPattern: {
|
|
3965
|
+
source: [USER_ONBOARDING_EVENT_SOURCE],
|
|
3966
|
+
detailType: [PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE]
|
|
3967
|
+
},
|
|
3968
|
+
targets: [
|
|
3969
|
+
new import_aws_events_targets4.LambdaFunction(this.lambda, {
|
|
3970
|
+
retryAttempts: 2,
|
|
3971
|
+
maxEventAge: import_aws_cdk_lib14.Duration.hours(2)
|
|
3972
|
+
})
|
|
3973
|
+
]
|
|
3974
|
+
});
|
|
3975
|
+
}
|
|
3976
|
+
};
|
|
3977
|
+
|
|
3978
|
+
// src/workflows/control-plane/user-onboarding/user-onboarding-workflow.ts
|
|
3979
|
+
var import_constructs16 = require("constructs");
|
|
3980
|
+
var UserOnboardingWorkflow = class extends import_constructs16.Construct {
|
|
3981
|
+
constructor(scope, props) {
|
|
3982
|
+
super(scope, "user-onboarding-workflow");
|
|
3983
|
+
this.provisionDefaultWorkspace = new ProvisionDefaultWorkspaceLambda(this, {
|
|
3984
|
+
dataStoreTable: props.dataStoreTable,
|
|
3985
|
+
controlEventBus: props.controlEventBus
|
|
3986
|
+
});
|
|
3987
|
+
}
|
|
3988
|
+
};
|
|
3989
|
+
|
|
1610
3990
|
// src/services/open-hi-auth-service.ts
|
|
1611
3991
|
var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
1612
3992
|
constructor(ohEnv, props = {}) {
|
|
@@ -1618,11 +3998,13 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1618
3998
|
* would collide.
|
|
1619
3999
|
*/
|
|
1620
4000
|
this._dataStoreTable = null;
|
|
4001
|
+
this._controlEventBus = null;
|
|
1621
4002
|
this.props = props;
|
|
1622
4003
|
this.userPoolKmsKey = this.createUserPoolKmsKey();
|
|
1623
4004
|
this.preTokenGenerationLambda = this.createPreTokenGenerationLambda();
|
|
1624
4005
|
this.postAuthenticationLambda = this.createPostAuthenticationLambda();
|
|
1625
4006
|
this.postConfirmationLambda = this.createPostConfirmationLambda();
|
|
4007
|
+
this.userOnboardingWorkflow = this.createUserOnboardingWorkflow();
|
|
1626
4008
|
this.userPool = this.createUserPool();
|
|
1627
4009
|
this.grantPreTokenGenerationPermissions();
|
|
1628
4010
|
this.grantPostAuthenticationPermissions();
|
|
@@ -1739,23 +4121,33 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1739
4121
|
}
|
|
1740
4122
|
/**
|
|
1741
4123
|
* Creates the Post Confirmation Lambda (Cognito trigger). On sign-up
|
|
1742
|
-
* confirmation,
|
|
1743
|
-
*
|
|
1744
|
-
* carrying the Cognito `sub` and current tenant/workspace pointers
|
|
1745
|
-
* (ADR 2026-03-17-01 invariants).
|
|
4124
|
+
* confirmation, publishes a control-plane workflow event; provisioning lives
|
|
4125
|
+
* behind EventBridge.
|
|
1746
4126
|
*/
|
|
1747
4127
|
createPostConfirmationLambda() {
|
|
1748
4128
|
const construct = new PostConfirmationLambda(this, {
|
|
1749
|
-
|
|
4129
|
+
controlEventBusName: this.controlEventBus().eventBusName
|
|
1750
4130
|
});
|
|
1751
4131
|
return construct.lambda;
|
|
1752
4132
|
}
|
|
4133
|
+
createUserOnboardingWorkflow() {
|
|
4134
|
+
return new UserOnboardingWorkflow(this, {
|
|
4135
|
+
controlEventBus: this.controlEventBus(),
|
|
4136
|
+
dataStoreTable: this.dataStoreTable()
|
|
4137
|
+
});
|
|
4138
|
+
}
|
|
1753
4139
|
dataStoreTable() {
|
|
1754
4140
|
if (this._dataStoreTable === null) {
|
|
1755
4141
|
this._dataStoreTable = OpenHiDataService.dynamoDbDataStoreFromConstruct(this);
|
|
1756
4142
|
}
|
|
1757
4143
|
return this._dataStoreTable;
|
|
1758
4144
|
}
|
|
4145
|
+
controlEventBus() {
|
|
4146
|
+
if (this._controlEventBus === null) {
|
|
4147
|
+
this._controlEventBus = OpenHiGlobalService.controlEventBusFromConstruct(this);
|
|
4148
|
+
}
|
|
4149
|
+
return this._controlEventBus;
|
|
4150
|
+
}
|
|
1759
4151
|
/**
|
|
1760
4152
|
* Creates the Cognito User Pool and exports its ID to SSM.
|
|
1761
4153
|
* Look up via {@link OpenHiAuthService.userPoolFromConstruct}.
|
|
@@ -1800,8 +4192,8 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1800
4192
|
const dynamoActions = ["dynamodb:GetItem", "dynamodb:Query"];
|
|
1801
4193
|
dataStoreTable.grant(this.preTokenGenerationLambda, ...dynamoActions);
|
|
1802
4194
|
this.preTokenGenerationLambda.addToRolePolicy(
|
|
1803
|
-
new
|
|
1804
|
-
effect:
|
|
4195
|
+
new import_aws_iam6.PolicyStatement({
|
|
4196
|
+
effect: import_aws_iam6.Effect.ALLOW,
|
|
1805
4197
|
actions: [...dynamoActions],
|
|
1806
4198
|
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
1807
4199
|
})
|
|
@@ -1822,7 +4214,7 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1822
4214
|
*/
|
|
1823
4215
|
grantPostAuthenticationPermissions() {
|
|
1824
4216
|
this.postAuthenticationLambda.addToRolePolicy(
|
|
1825
|
-
new
|
|
4217
|
+
new import_aws_iam6.PolicyStatement({
|
|
1826
4218
|
actions: ["cognito-idp:AdminUserGlobalSignOut"],
|
|
1827
4219
|
resources: [
|
|
1828
4220
|
import_core2.Stack.of(this).formatArn({
|
|
@@ -1835,26 +4227,11 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1835
4227
|
);
|
|
1836
4228
|
}
|
|
1837
4229
|
/**
|
|
1838
|
-
* Grants the Post Confirmation Lambda
|
|
1839
|
-
*
|
|
1840
|
-
* Memberships, RoleAssignment, and User records on sign-up confirmation.
|
|
4230
|
+
* Grants the Post Confirmation Lambda publish-only access to the
|
|
4231
|
+
* control-plane event bus. Workflow Lambdas own DynamoDB writes.
|
|
1841
4232
|
*/
|
|
1842
4233
|
grantPostConfirmationPermissions() {
|
|
1843
|
-
|
|
1844
|
-
const dynamoActions = [
|
|
1845
|
-
"dynamodb:PutItem",
|
|
1846
|
-
"dynamodb:UpdateItem",
|
|
1847
|
-
"dynamodb:BatchWriteItem",
|
|
1848
|
-
"dynamodb:DescribeTable"
|
|
1849
|
-
];
|
|
1850
|
-
dataStoreTable.grant(this.postConfirmationLambda, ...dynamoActions);
|
|
1851
|
-
this.postConfirmationLambda.addToRolePolicy(
|
|
1852
|
-
new import_aws_iam.PolicyStatement({
|
|
1853
|
-
effect: import_aws_iam.Effect.ALLOW,
|
|
1854
|
-
actions: [...dynamoActions],
|
|
1855
|
-
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
1856
|
-
})
|
|
1857
|
-
);
|
|
4234
|
+
this.controlEventBus().grantPutEventsTo(this.postConfirmationLambda);
|
|
1858
4235
|
}
|
|
1859
4236
|
/**
|
|
1860
4237
|
* Creates the User Pool Client and exports its ID to SSM (AUTH service type).
|
|
@@ -1884,7 +4261,7 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
|
1884
4261
|
* via env vars to drive `InitiateAuth`.
|
|
1885
4262
|
*/
|
|
1886
4263
|
createFixtureSeederClient() {
|
|
1887
|
-
if (this.ohEnv.ohStage.stageType ===
|
|
4264
|
+
if (this.ohEnv.ohStage.stageType === import_config5.OPEN_HI_STAGE.PROD) {
|
|
1888
4265
|
return void 0;
|
|
1889
4266
|
}
|
|
1890
4267
|
const client = new CognitoFixtureSeederClient(this, {
|
|
@@ -1921,62 +4298,62 @@ _OpenHiAuthService.SERVICE_TYPE = "auth";
|
|
|
1921
4298
|
var OpenHiAuthService = _OpenHiAuthService;
|
|
1922
4299
|
|
|
1923
4300
|
// src/services/open-hi-rest-api-service.ts
|
|
1924
|
-
var
|
|
4301
|
+
var import_config6 = __toESM(require_lib());
|
|
1925
4302
|
var import_aws_apigatewayv22 = require("aws-cdk-lib/aws-apigatewayv2");
|
|
1926
4303
|
var import_aws_apigatewayv2_authorizers = require("aws-cdk-lib/aws-apigatewayv2-authorizers");
|
|
1927
4304
|
var import_aws_apigatewayv2_integrations = require("aws-cdk-lib/aws-apigatewayv2-integrations");
|
|
1928
|
-
var
|
|
4305
|
+
var import_aws_iam7 = require("aws-cdk-lib/aws-iam");
|
|
1929
4306
|
var import_aws_route533 = require("aws-cdk-lib/aws-route53");
|
|
1930
4307
|
var import_aws_route53_targets = require("aws-cdk-lib/aws-route53-targets");
|
|
1931
4308
|
var import_core3 = require("aws-cdk-lib/core");
|
|
1932
4309
|
|
|
1933
4310
|
// src/data/lambda/cors-options-lambda.ts
|
|
1934
|
-
var
|
|
1935
|
-
var
|
|
1936
|
-
var
|
|
1937
|
-
var
|
|
1938
|
-
var
|
|
1939
|
-
var
|
|
1940
|
-
function
|
|
1941
|
-
const sameDir =
|
|
1942
|
-
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)) {
|
|
1943
4320
|
return sameDir;
|
|
1944
4321
|
}
|
|
1945
|
-
const fromLib =
|
|
4322
|
+
const fromLib = import_node_path10.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME10);
|
|
1946
4323
|
return fromLib;
|
|
1947
4324
|
}
|
|
1948
|
-
var CorsOptionsLambda = class extends
|
|
4325
|
+
var CorsOptionsLambda = class extends import_constructs17.Construct {
|
|
1949
4326
|
constructor(scope, id = "cors-options-lambda") {
|
|
1950
4327
|
super(scope, id);
|
|
1951
|
-
this.lambda = new
|
|
1952
|
-
entry:
|
|
1953
|
-
runtime:
|
|
4328
|
+
this.lambda = new import_aws_lambda_nodejs10.NodejsFunction(this, "handler", {
|
|
4329
|
+
entry: resolveHandlerEntry10(__dirname),
|
|
4330
|
+
runtime: import_aws_lambda10.Runtime.NODEJS_LATEST,
|
|
1954
4331
|
memorySize: 128
|
|
1955
4332
|
});
|
|
1956
4333
|
}
|
|
1957
4334
|
};
|
|
1958
4335
|
|
|
1959
4336
|
// src/data/lambda/rest-api-lambda.ts
|
|
1960
|
-
var
|
|
1961
|
-
var
|
|
1962
|
-
var
|
|
1963
|
-
var
|
|
1964
|
-
var
|
|
1965
|
-
var
|
|
1966
|
-
function
|
|
1967
|
-
const sameDir =
|
|
1968
|
-
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)) {
|
|
1969
4346
|
return sameDir;
|
|
1970
4347
|
}
|
|
1971
|
-
const fromLib =
|
|
4348
|
+
const fromLib = import_node_path11.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME11);
|
|
1972
4349
|
return fromLib;
|
|
1973
4350
|
}
|
|
1974
|
-
var RestApiLambda = class extends
|
|
4351
|
+
var RestApiLambda = class extends import_constructs18.Construct {
|
|
1975
4352
|
constructor(scope, props) {
|
|
1976
4353
|
super(scope, "rest-api-lambda");
|
|
1977
|
-
this.lambda = new
|
|
1978
|
-
entry:
|
|
1979
|
-
runtime:
|
|
4354
|
+
this.lambda = new import_aws_lambda_nodejs11.NodejsFunction(this, "handler", {
|
|
4355
|
+
entry: resolveHandlerEntry11(__dirname),
|
|
4356
|
+
runtime: import_aws_lambda11.Runtime.NODEJS_LATEST,
|
|
1980
4357
|
memorySize: 1024,
|
|
1981
4358
|
environment: {
|
|
1982
4359
|
DYNAMO_TABLE_NAME: props.dynamoTableName,
|
|
@@ -2118,8 +4495,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2118
4495
|
postgresSchema
|
|
2119
4496
|
});
|
|
2120
4497
|
lambda.addToRolePolicy(
|
|
2121
|
-
new
|
|
2122
|
-
effect:
|
|
4498
|
+
new import_aws_iam7.PolicyStatement({
|
|
4499
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2123
4500
|
actions: [
|
|
2124
4501
|
"rds-data:ExecuteStatement",
|
|
2125
4502
|
"rds-data:BatchExecuteStatement"
|
|
@@ -2128,8 +4505,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2128
4505
|
})
|
|
2129
4506
|
);
|
|
2130
4507
|
lambda.addToRolePolicy(
|
|
2131
|
-
new
|
|
2132
|
-
effect:
|
|
4508
|
+
new import_aws_iam7.PolicyStatement({
|
|
4509
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2133
4510
|
actions: ["secretsmanager:GetSecretValue"],
|
|
2134
4511
|
resources: [postgresSecretArn]
|
|
2135
4512
|
})
|
|
@@ -2147,15 +4524,15 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2147
4524
|
];
|
|
2148
4525
|
dataStoreTable.grant(lambda, ...dynamoActions);
|
|
2149
4526
|
lambda.addToRolePolicy(
|
|
2150
|
-
new
|
|
2151
|
-
effect:
|
|
4527
|
+
new import_aws_iam7.PolicyStatement({
|
|
4528
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2152
4529
|
actions: [...dynamoActions],
|
|
2153
4530
|
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
2154
4531
|
})
|
|
2155
4532
|
);
|
|
2156
4533
|
lambda.addToRolePolicy(
|
|
2157
|
-
new
|
|
2158
|
-
effect:
|
|
4534
|
+
new import_aws_iam7.PolicyStatement({
|
|
4535
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
2159
4536
|
actions: [
|
|
2160
4537
|
"ssm:GetParameter",
|
|
2161
4538
|
"ssm:GetParameters",
|
|
@@ -2214,7 +4591,7 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
2214
4591
|
const userPool = OpenHiAuthService.userPoolFromConstruct(this);
|
|
2215
4592
|
const userPoolClient = OpenHiAuthService.userPoolClientFromConstruct(this);
|
|
2216
4593
|
const userPoolClients = [userPoolClient];
|
|
2217
|
-
if (this.ohEnv.ohStage.stageType !==
|
|
4594
|
+
if (this.ohEnv.ohStage.stageType !== import_config6.OPEN_HI_STAGE.PROD) {
|
|
2218
4595
|
userPoolClients.push(
|
|
2219
4596
|
OpenHiAuthService.fixtureSeederClientFromConstruct(this)
|
|
2220
4597
|
);
|
|
@@ -2304,20 +4681,36 @@ _OpenHiGraphqlService.SERVICE_TYPE = "graphql-api";
|
|
|
2304
4681
|
var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
2305
4682
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2306
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,
|
|
2307
4688
|
ChildHostedZone,
|
|
2308
4689
|
CognitoFixtureSeederClient,
|
|
2309
4690
|
CognitoUserPool,
|
|
2310
4691
|
CognitoUserPoolClient,
|
|
2311
4692
|
CognitoUserPoolDomain,
|
|
2312
4693
|
CognitoUserPoolKmsKey,
|
|
4694
|
+
ControlEventBus,
|
|
2313
4695
|
DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES,
|
|
2314
4696
|
DATA_STORE_CHANGE_DETAIL_TYPE,
|
|
2315
4697
|
DATA_STORE_CHANGE_EVENT_SOURCE,
|
|
4698
|
+
DEMO_PERIOD,
|
|
4699
|
+
DEMO_TENANT_SPECS,
|
|
4700
|
+
DEMO_URN_SYSTEM,
|
|
4701
|
+
DEV_USERS,
|
|
2316
4702
|
DataEventBus,
|
|
2317
4703
|
DataStoreHistoricalArchive,
|
|
2318
4704
|
DataStorePostgresReplica,
|
|
2319
4705
|
DiscoverableStringParameter,
|
|
2320
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,
|
|
2321
4714
|
OpenHiApp,
|
|
2322
4715
|
OpenHiAuthService,
|
|
2323
4716
|
OpenHiDataService,
|
|
@@ -2328,21 +4721,59 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
|
2328
4721
|
OpenHiService,
|
|
2329
4722
|
OpenHiStage,
|
|
2330
4723
|
OpsEventBus,
|
|
4724
|
+
PLACEHOLDER_TENANT_ID,
|
|
4725
|
+
PLACEHOLDER_WORKSPACE_ID,
|
|
4726
|
+
PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM,
|
|
4727
|
+
PLATFORM_SCOPE_TENANT_ID,
|
|
2331
4728
|
POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
|
|
2332
4729
|
POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
|
|
2333
4730
|
POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
|
|
4731
|
+
PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE,
|
|
4732
|
+
PlatformDeployBridge,
|
|
4733
|
+
PlatformDeployBridgeLambda,
|
|
4734
|
+
PlatformDeploymentCompletedV1,
|
|
2334
4735
|
PostAuthenticationLambda,
|
|
2335
4736
|
PostConfirmationLambda,
|
|
2336
4737
|
PreTokenGenerationLambda,
|
|
4738
|
+
ProvisionDefaultWorkspaceLambda,
|
|
2337
4739
|
REST_API_BASE_URL_SSM_NAME,
|
|
2338
4740
|
RootGraphqlApi,
|
|
2339
4741
|
RootHostedZone,
|
|
2340
4742
|
RootHttpApi,
|
|
2341
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,
|
|
2342
4748
|
STATIC_HOSTING_SERVICE_TYPE,
|
|
4749
|
+
SeedDemoDataLambda,
|
|
4750
|
+
SeedDemoDataWorkflow,
|
|
4751
|
+
SeedSystemDataLambda,
|
|
4752
|
+
SeedSystemDataWorkflow,
|
|
2343
4753
|
StaticHosting,
|
|
4754
|
+
USER_ONBOARDING_EVENT_SOURCE,
|
|
4755
|
+
UserOnboardingWorkflow,
|
|
4756
|
+
WorkflowDedupConsumerNameInvalidError,
|
|
4757
|
+
WorkflowDedupTable,
|
|
4758
|
+
WorkflowDedupTableDuplicateError,
|
|
2344
4759
|
buildFhirCurrentResourceChangeDetail,
|
|
4760
|
+
buildProvisionDefaultWorkspaceRequestedDetail,
|
|
4761
|
+
demoBasePartitionKeys,
|
|
4762
|
+
demoDevUserPartitionKeys,
|
|
4763
|
+
demoMembershipId,
|
|
4764
|
+
demoMembershipPartitionKey,
|
|
4765
|
+
demoRoleAssignmentId,
|
|
4766
|
+
demoRoleAssignmentPartitionKey,
|
|
4767
|
+
demoRolesForUserInTenant,
|
|
4768
|
+
demoScenarioIdentifier,
|
|
4769
|
+
demoTenantPartitionKey,
|
|
4770
|
+
demoUserPartitionKey,
|
|
4771
|
+
demoWorkspacePartitionKey,
|
|
2345
4772
|
getDynamoDbDataStoreTableName,
|
|
2346
|
-
getPostgresReplicaSchemaName
|
|
4773
|
+
getPostgresReplicaSchemaName,
|
|
4774
|
+
getWorkflowDedupTableName,
|
|
4775
|
+
openHiTagKey,
|
|
4776
|
+
openhiResourceIdentifier,
|
|
4777
|
+
rolePartitionKey
|
|
2347
4778
|
});
|
|
2348
4779
|
//# sourceMappingURL=index.js.map
|