@openhi/constructs 0.0.151 → 0.0.152
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/{chunk-AWYZJFPL.mjs → chunk-CFJDATDK.mjs} +8 -1
- package/lib/chunk-CFJDATDK.mjs.map +1 -0
- package/lib/{chunk-WGA43MMY.mjs → chunk-SXYY5WHG.mjs} +194 -71
- package/lib/chunk-SXYY5WHG.mjs.map +1 -0
- package/lib/{events-CMG8xanm.d.mts → events-DTgo2dcW.d.mts} +2 -14
- package/lib/{events-CMG8xanm.d.ts → events-DTgo2dcW.d.ts} +2 -14
- package/lib/index.d.mts +54 -7
- package/lib/index.d.ts +53 -18
- package/lib/index.js +53 -19
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +35 -4
- package/lib/index.mjs.map +1 -1
- package/lib/provision-default-workspace.handler.js +6 -0
- package/lib/provision-default-workspace.handler.js.map +1 -1
- package/lib/provision-default-workspace.handler.mjs +1 -1
- package/lib/rest-api-lambda.handler.js +6 -0
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +1 -1
- package/lib/seed-demo-data.handler.d.mts +14 -1
- package/lib/seed-demo-data.handler.d.ts +14 -1
- package/lib/seed-demo-data.handler.js +199 -68
- package/lib/seed-demo-data.handler.js.map +1 -1
- package/lib/seed-demo-data.handler.mjs +2 -2
- package/package.json +5 -5
- package/lib/chunk-AWYZJFPL.mjs.map +0 -1
- package/lib/chunk-WGA43MMY.mjs.map +0 -1
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
createTenantOperation,
|
|
24
24
|
createWorkspaceOperation,
|
|
25
25
|
extractDenormalizedReferenceDisplay
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-CFJDATDK.mjs";
|
|
27
27
|
import {
|
|
28
28
|
buildMembershipUserProjectionItem,
|
|
29
29
|
buildMembershipWorkspaceProjectionItem,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WorkflowDedupClient } from '@openhi/workflows';
|
|
2
2
|
import { EventBridgeEvent } from 'aws-lambda';
|
|
3
|
-
import { d as DemoDevUser } from './events-
|
|
3
|
+
import { d as DemoDevUser } from './events-DTgo2dcW.mjs';
|
|
4
4
|
import { O as OpenHiContext } from './openhi-context-CaBH8SFo.mjs';
|
|
5
5
|
import '@openhi/types';
|
|
6
6
|
|
|
@@ -68,6 +68,19 @@ interface SeedDemoDataDependencies {
|
|
|
68
68
|
* put is keyed by a deterministic stable id so re-runs after
|
|
69
69
|
* dedup-TTL expiry upsert the same records.
|
|
70
70
|
*
|
|
71
|
+
* Self-healing-on-every-deploy contract: every individual upsert
|
|
72
|
+
* across all three phases is wrapped via {@link tryRun}. A single
|
|
73
|
+
* item's failure (transient AWS error, write-time constraint
|
|
74
|
+
* violation, etc.) is collected into a per-call accumulator and
|
|
75
|
+
* does not skip the remaining items in its phase. After all phases
|
|
76
|
+
* run, collected failures are aggregate-thrown as a single error so
|
|
77
|
+
* EventBridge still routes the workflow into its failure-detection
|
|
78
|
+
* path (DLQ + CloudWatch alarm) and the outer `runSeedDemoData`
|
|
79
|
+
* records `markFailed` on the dedup row. Because every put is keyed
|
|
80
|
+
* by a deterministic stable id, the next deploy re-attempts every
|
|
81
|
+
* item — failed items eventually heal, successful items overwrite
|
|
82
|
+
* themselves with the same body.
|
|
83
|
+
*
|
|
71
84
|
* Exported so the seeder test file can exercise it directly against
|
|
72
85
|
* a mocked DynamoControlService; the production handler reaches it
|
|
73
86
|
* through {@link SeedDemoDataDependencies.seedDemoGraph}.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WorkflowDedupClient } from '@openhi/workflows';
|
|
2
2
|
import { EventBridgeEvent } from 'aws-lambda';
|
|
3
|
-
import { d as DemoDevUser } from './events-
|
|
3
|
+
import { d as DemoDevUser } from './events-DTgo2dcW.js';
|
|
4
4
|
import { O as OpenHiContext } from './openhi-context-CaBH8SFo.js';
|
|
5
5
|
import '@openhi/types';
|
|
6
6
|
|
|
@@ -68,6 +68,19 @@ interface SeedDemoDataDependencies {
|
|
|
68
68
|
* put is keyed by a deterministic stable id so re-runs after
|
|
69
69
|
* dedup-TTL expiry upsert the same records.
|
|
70
70
|
*
|
|
71
|
+
* Self-healing-on-every-deploy contract: every individual upsert
|
|
72
|
+
* across all three phases is wrapped via {@link tryRun}. A single
|
|
73
|
+
* item's failure (transient AWS error, write-time constraint
|
|
74
|
+
* violation, etc.) is collected into a per-call accumulator and
|
|
75
|
+
* does not skip the remaining items in its phase. After all phases
|
|
76
|
+
* run, collected failures are aggregate-thrown as a single error so
|
|
77
|
+
* EventBridge still routes the workflow into its failure-detection
|
|
78
|
+
* path (DLQ + CloudWatch alarm) and the outer `runSeedDemoData`
|
|
79
|
+
* records `markFailed` on the dedup row. Because every put is keyed
|
|
80
|
+
* by a deterministic stable id, the next deploy re-attempts every
|
|
81
|
+
* item — failed items eventually heal, successful items overwrite
|
|
82
|
+
* themselves with the same body.
|
|
83
|
+
*
|
|
71
84
|
* Exported so the seeder test file can exercise it directly against
|
|
72
85
|
* a mocked DynamoControlService; the production handler reaches it
|
|
73
86
|
* through {@link SeedDemoDataDependencies.seedDemoGraph}.
|
|
@@ -723,11 +723,15 @@ var import_workflows2 = __toESM(require_lib());
|
|
|
723
723
|
// src/workflows/control-plane/seed-demo-data/events.ts
|
|
724
724
|
var import_types = require("@openhi/types");
|
|
725
725
|
var import_workflows = __toESM(require_lib());
|
|
726
|
+
|
|
727
|
+
// src/data/operations/control/membership-constraints/platform-scope-tenant-id.ts
|
|
728
|
+
var PLATFORM_SCOPE_TENANT_ID = "platform";
|
|
729
|
+
|
|
730
|
+
// src/workflows/control-plane/seed-demo-data/events.ts
|
|
726
731
|
var SEED_DEMO_DATA_CONSUMER_NAME = "seed-demo-data";
|
|
727
732
|
var DEMO_URN_SYSTEM = "urn:openhi:demo";
|
|
728
733
|
var OPENHI_RESOURCE_URN_SYSTEM = "http://openhi.org/";
|
|
729
734
|
var DEMO_PERIOD = { start: "2026-01-01T00:00:00Z" };
|
|
730
|
-
var PLATFORM_SCOPE_TENANT_ID = "platform";
|
|
731
735
|
var PLACEHOLDER_TENANT_ID = "placeholder-tenant-id";
|
|
732
736
|
var PLACEHOLDER_WORKSPACE_ID = "placeholder-workspace-id";
|
|
733
737
|
var DEV_USERS = [
|
|
@@ -3510,6 +3514,9 @@ function buildRoleAssignmentWorkspaceProjectionItem(input) {
|
|
|
3510
3514
|
var TENANT_LANE_SK_PREFIX = "MEMBERSHIP#TENANT#";
|
|
3511
3515
|
async function assertUserHasTenantMembershipOperation(params) {
|
|
3512
3516
|
const { userId, tenantId, tableName } = params;
|
|
3517
|
+
if (tenantId === PLATFORM_SCOPE_TENANT_ID) {
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3513
3520
|
const service = getDynamoControlService(tableName);
|
|
3514
3521
|
const result = await service.entities.membershipUserProjection.query.record({ userId }).begins({ sk: TENANT_LANE_SK_PREFIX }).go();
|
|
3515
3522
|
const matched = (result.data ?? []).some((row) => row.tenantId === tenantId);
|
|
@@ -5017,6 +5024,25 @@ var errorMessage = (err) => {
|
|
|
5017
5024
|
}
|
|
5018
5025
|
return String(err);
|
|
5019
5026
|
};
|
|
5027
|
+
var tryRun = async (failures, phase, scope, resourceType, resourceId, fn) => {
|
|
5028
|
+
try {
|
|
5029
|
+
await fn();
|
|
5030
|
+
return true;
|
|
5031
|
+
} catch (err) {
|
|
5032
|
+
failures.push({ phase, scope, resourceType, resourceId, error: err });
|
|
5033
|
+
return false;
|
|
5034
|
+
}
|
|
5035
|
+
};
|
|
5036
|
+
var aggregateFailureError = (failures) => {
|
|
5037
|
+
const summary = failures.map(
|
|
5038
|
+
(f) => `${f.phase} ${f.scope}/${f.resourceType}/${f.resourceId}: ${errorMessage(
|
|
5039
|
+
f.error
|
|
5040
|
+
)}`
|
|
5041
|
+
).join("; ");
|
|
5042
|
+
return new Error(
|
|
5043
|
+
`seed-demo-data: ${failures.length} item(s) failed across phases: ${summary}`
|
|
5044
|
+
);
|
|
5045
|
+
};
|
|
5020
5046
|
var idForRoleCode = (code) => {
|
|
5021
5047
|
for (const key of Object.keys(import_types12.PLATFORM_ROLE_IDS)) {
|
|
5022
5048
|
if (import_types12.PLATFORM_ROLE_CONCEPTS[key].code === code) {
|
|
@@ -5126,95 +5152,180 @@ var upsertUser = async (context, user, cognitoSub) => {
|
|
|
5126
5152
|
lastUpdated: context.date ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
5127
5153
|
}).go();
|
|
5128
5154
|
};
|
|
5129
|
-
var seedWorkspaceDataPlane = async (baseContext, group) => {
|
|
5155
|
+
var seedWorkspaceDataPlane = async (baseContext, group, failures) => {
|
|
5130
5156
|
const workspaceContext = {
|
|
5131
5157
|
...baseContext,
|
|
5132
5158
|
tenantId: group.tenantId,
|
|
5133
5159
|
workspaceId: group.workspaceId
|
|
5134
5160
|
};
|
|
5161
|
+
const scope = `${group.tenantId}/${group.workspaceId}`;
|
|
5135
5162
|
for (const patient of group.patients) {
|
|
5136
|
-
await
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5163
|
+
await tryRun(
|
|
5164
|
+
failures,
|
|
5165
|
+
"phase-3",
|
|
5166
|
+
scope,
|
|
5167
|
+
"Patient",
|
|
5168
|
+
patient.id ?? "",
|
|
5169
|
+
() => createPatientOperation({
|
|
5170
|
+
context: workspaceContext,
|
|
5171
|
+
body: patient
|
|
5172
|
+
})
|
|
5173
|
+
);
|
|
5140
5174
|
}
|
|
5141
5175
|
for (const practitioner of group.practitioners) {
|
|
5142
|
-
await
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5176
|
+
await tryRun(
|
|
5177
|
+
failures,
|
|
5178
|
+
"phase-3",
|
|
5179
|
+
scope,
|
|
5180
|
+
"Practitioner",
|
|
5181
|
+
practitioner.id ?? "",
|
|
5182
|
+
() => createPractitionerOperation({
|
|
5183
|
+
context: workspaceContext,
|
|
5184
|
+
body: practitioner
|
|
5185
|
+
})
|
|
5186
|
+
);
|
|
5146
5187
|
}
|
|
5147
5188
|
for (const observation of group.observations) {
|
|
5148
|
-
await
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5189
|
+
await tryRun(
|
|
5190
|
+
failures,
|
|
5191
|
+
"phase-3",
|
|
5192
|
+
scope,
|
|
5193
|
+
"Observation",
|
|
5194
|
+
observation.id ?? "",
|
|
5195
|
+
() => createObservationOperation({
|
|
5196
|
+
context: workspaceContext,
|
|
5197
|
+
body: observation
|
|
5198
|
+
})
|
|
5199
|
+
);
|
|
5152
5200
|
}
|
|
5153
5201
|
for (const encounter of group.encounters) {
|
|
5154
|
-
await
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5202
|
+
await tryRun(
|
|
5203
|
+
failures,
|
|
5204
|
+
"phase-3",
|
|
5205
|
+
scope,
|
|
5206
|
+
"Encounter",
|
|
5207
|
+
encounter.id ?? "",
|
|
5208
|
+
() => createEncounterOperation({
|
|
5209
|
+
context: workspaceContext,
|
|
5210
|
+
body: encounter
|
|
5211
|
+
})
|
|
5212
|
+
);
|
|
5158
5213
|
}
|
|
5159
5214
|
for (const account of group.accounts) {
|
|
5160
|
-
await
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5215
|
+
await tryRun(
|
|
5216
|
+
failures,
|
|
5217
|
+
"phase-3",
|
|
5218
|
+
scope,
|
|
5219
|
+
"Account",
|
|
5220
|
+
account.id ?? "",
|
|
5221
|
+
() => createAccountOperation({
|
|
5222
|
+
context: workspaceContext,
|
|
5223
|
+
body: account
|
|
5224
|
+
})
|
|
5225
|
+
);
|
|
5164
5226
|
}
|
|
5165
5227
|
};
|
|
5166
5228
|
var seedDemoGraph = async (params) => {
|
|
5167
5229
|
const { baseContext, devUsers, cognito } = params;
|
|
5230
|
+
const failures = [];
|
|
5168
5231
|
for (const spec of DEMO_TENANT_SPECS) {
|
|
5169
5232
|
const tenantContext = {
|
|
5170
5233
|
...baseContext,
|
|
5171
5234
|
tenantId: spec.tenantId
|
|
5172
5235
|
};
|
|
5173
|
-
await
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5236
|
+
await tryRun(
|
|
5237
|
+
failures,
|
|
5238
|
+
"phase-1",
|
|
5239
|
+
spec.tenantId,
|
|
5240
|
+
"Tenant",
|
|
5241
|
+
spec.tenantId,
|
|
5242
|
+
() => createTenantOperation({
|
|
5179
5243
|
context: tenantContext,
|
|
5180
|
-
body: {
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5244
|
+
body: { id: spec.tenantId, resource: tenantResourceBody(spec) }
|
|
5245
|
+
})
|
|
5246
|
+
);
|
|
5247
|
+
for (const workspace of spec.workspaces) {
|
|
5248
|
+
await tryRun(
|
|
5249
|
+
failures,
|
|
5250
|
+
"phase-1",
|
|
5251
|
+
spec.tenantId,
|
|
5252
|
+
"Workspace",
|
|
5253
|
+
workspace.id,
|
|
5254
|
+
() => createWorkspaceOperation({
|
|
5255
|
+
context: tenantContext,
|
|
5256
|
+
body: {
|
|
5257
|
+
id: workspace.id,
|
|
5258
|
+
resource: workspaceResourceBody(spec, workspace)
|
|
5259
|
+
}
|
|
5260
|
+
})
|
|
5261
|
+
);
|
|
5185
5262
|
}
|
|
5186
5263
|
}
|
|
5187
5264
|
for (const user of devUsers) {
|
|
5188
|
-
|
|
5189
|
-
|
|
5265
|
+
let cognitoSub;
|
|
5266
|
+
try {
|
|
5267
|
+
cognitoSub = await cognito.ensureUser(user.email);
|
|
5268
|
+
} catch (err) {
|
|
5269
|
+
failures.push({
|
|
5270
|
+
phase: "phase-2",
|
|
5271
|
+
scope: user.id,
|
|
5272
|
+
resourceType: "CognitoUser",
|
|
5273
|
+
resourceId: user.email,
|
|
5274
|
+
error: err
|
|
5275
|
+
});
|
|
5276
|
+
continue;
|
|
5277
|
+
}
|
|
5278
|
+
await tryRun(
|
|
5279
|
+
failures,
|
|
5280
|
+
"phase-2",
|
|
5281
|
+
user.id,
|
|
5282
|
+
"User",
|
|
5283
|
+
user.id,
|
|
5284
|
+
() => upsertUser(baseContext, user, cognitoSub)
|
|
5285
|
+
);
|
|
5190
5286
|
for (const spec of DEMO_TENANT_SPECS) {
|
|
5191
5287
|
const tenantContext = {
|
|
5192
5288
|
...baseContext,
|
|
5193
5289
|
tenantId: spec.tenantId
|
|
5194
5290
|
};
|
|
5291
|
+
const userScope = `${user.id}@${spec.tenantId}`;
|
|
5195
5292
|
const membershipId = demoMembershipId(user.id, spec.tenantId);
|
|
5196
|
-
await
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {
|
|
5204
|
-
const raId = demoRoleAssignmentId(user.id, spec.tenantId, roleCode);
|
|
5205
|
-
await createRoleAssignmentOperation({
|
|
5293
|
+
await tryRun(
|
|
5294
|
+
failures,
|
|
5295
|
+
"phase-2",
|
|
5296
|
+
userScope,
|
|
5297
|
+
"Membership",
|
|
5298
|
+
membershipId,
|
|
5299
|
+
() => createMembershipOperation({
|
|
5206
5300
|
context: tenantContext,
|
|
5207
5301
|
body: {
|
|
5208
|
-
id:
|
|
5209
|
-
resource:
|
|
5210
|
-
spec.scenario,
|
|
5211
|
-
spec.tenantId,
|
|
5212
|
-
user,
|
|
5213
|
-
roleCode,
|
|
5214
|
-
raId
|
|
5215
|
-
)
|
|
5302
|
+
id: membershipId,
|
|
5303
|
+
resource: membershipResourceBody(spec, user, membershipId)
|
|
5216
5304
|
}
|
|
5217
|
-
})
|
|
5305
|
+
})
|
|
5306
|
+
);
|
|
5307
|
+
for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {
|
|
5308
|
+
const raId = demoRoleAssignmentId(user.id, spec.tenantId, roleCode);
|
|
5309
|
+
await tryRun(
|
|
5310
|
+
failures,
|
|
5311
|
+
"phase-2",
|
|
5312
|
+
userScope,
|
|
5313
|
+
"RoleAssignment",
|
|
5314
|
+
raId,
|
|
5315
|
+
() => createRoleAssignmentOperation({
|
|
5316
|
+
context: tenantContext,
|
|
5317
|
+
body: {
|
|
5318
|
+
id: raId,
|
|
5319
|
+
resource: roleAssignmentResourceBody(
|
|
5320
|
+
spec.scenario,
|
|
5321
|
+
spec.tenantId,
|
|
5322
|
+
user,
|
|
5323
|
+
roleCode,
|
|
5324
|
+
raId
|
|
5325
|
+
)
|
|
5326
|
+
}
|
|
5327
|
+
})
|
|
5328
|
+
);
|
|
5218
5329
|
}
|
|
5219
5330
|
}
|
|
5220
5331
|
const platformContext = {
|
|
@@ -5227,22 +5338,42 @@ var seedDemoGraph = async (params) => {
|
|
|
5227
5338
|
PLATFORM_SCOPE_TENANT_ID,
|
|
5228
5339
|
platformRoleCode
|
|
5229
5340
|
);
|
|
5230
|
-
await
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
platformRaId
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5341
|
+
await tryRun(
|
|
5342
|
+
failures,
|
|
5343
|
+
"phase-2",
|
|
5344
|
+
`${user.id}@${PLATFORM_SCOPE_TENANT_ID}`,
|
|
5345
|
+
"RoleAssignment",
|
|
5346
|
+
platformRaId,
|
|
5347
|
+
() => createRoleAssignmentOperation({
|
|
5348
|
+
context: platformContext,
|
|
5349
|
+
body: {
|
|
5350
|
+
id: platformRaId,
|
|
5351
|
+
resource: roleAssignmentResourceBody(
|
|
5352
|
+
"platform",
|
|
5353
|
+
PLATFORM_SCOPE_TENANT_ID,
|
|
5354
|
+
user,
|
|
5355
|
+
platformRoleCode,
|
|
5356
|
+
platformRaId
|
|
5357
|
+
)
|
|
5358
|
+
}
|
|
5359
|
+
})
|
|
5360
|
+
);
|
|
5243
5361
|
}
|
|
5244
5362
|
for (const group of DEMO_DATA_PLANE_FIXTURES) {
|
|
5245
|
-
|
|
5363
|
+
try {
|
|
5364
|
+
await seedWorkspaceDataPlane(baseContext, group, failures);
|
|
5365
|
+
} catch (err) {
|
|
5366
|
+
failures.push({
|
|
5367
|
+
phase: "phase-3",
|
|
5368
|
+
scope: `${group.tenantId}/${group.workspaceId}`,
|
|
5369
|
+
resourceType: "Workspace",
|
|
5370
|
+
resourceId: group.workspaceId,
|
|
5371
|
+
error: err
|
|
5372
|
+
});
|
|
5373
|
+
}
|
|
5374
|
+
}
|
|
5375
|
+
if (failures.length > 0) {
|
|
5376
|
+
throw aggregateFailureError(failures);
|
|
5246
5377
|
}
|
|
5247
5378
|
};
|
|
5248
5379
|
var runSeedDemoData = async (event, deps, devUsers) => {
|