@openhi/constructs 0.0.158 → 0.0.160

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.
@@ -1,1136 +0,0 @@
1
- import {
2
- createAccountOperation,
3
- createEncounterOperation,
4
- createObservationOperation,
5
- createPatientOperation,
6
- createPractitionerOperation,
7
- getRoleByIdOperation
8
- } from "./chunk-E6MCKJVS.mjs";
9
- import {
10
- require_lib
11
- } from "./chunk-ZM4GDHHC.mjs";
12
- import {
13
- PLATFORM_SCOPE_TENANT_ID,
14
- createMembershipOperation,
15
- createRoleAssignmentOperation,
16
- createTenantOperation,
17
- createWorkspaceOperation
18
- } from "./chunk-CFJDATDK.mjs";
19
- import {
20
- NotFoundError
21
- } from "./chunk-FYHBHHWK.mjs";
22
- import {
23
- getDynamoControlService
24
- } from "./chunk-VZCPGQXA.mjs";
25
- import {
26
- __toESM
27
- } from "./chunk-LZOMFHX3.mjs";
28
-
29
- // src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts
30
- var import_workflows2 = __toESM(require_lib());
31
- import {
32
- AdminCreateUserCommand,
33
- AdminGetUserCommand,
34
- AdminSetUserPasswordCommand,
35
- CognitoIdentityProviderClient,
36
- UsernameExistsException
37
- } from "@aws-sdk/client-cognito-identity-provider";
38
- import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
39
- import {
40
- GetParameterCommand,
41
- ParameterNotFound,
42
- SSMClient
43
- } from "@aws-sdk/client-ssm";
44
- import {
45
- PLATFORM_ROLE_CODE as PLATFORM_ROLE_CODE2,
46
- PLATFORM_ROLE_CONCEPTS,
47
- PLATFORM_ROLE_IDS,
48
- extractSummary
49
- } from "@openhi/types";
50
-
51
- // src/workflows/control-plane/seed-demo-data/events.ts
52
- var import_workflows = __toESM(require_lib());
53
- import { PLATFORM_ROLE_CODE } from "@openhi/types";
54
- var SEED_DEMO_DATA_CONSUMER_NAME = "seed-demo-data";
55
- var DEMO_URN_SYSTEM = "urn:openhi:demo";
56
- var OPENHI_RESOURCE_URN_SYSTEM = "http://openhi.org/";
57
- var DEMO_PERIOD = { start: "2026-01-01T00:00:00Z" };
58
- var PLACEHOLDER_TENANT_ID = "placeholder-tenant-id";
59
- var PLACEHOLDER_WORKSPACE_ID = "placeholder-workspace-id";
60
- var DEV_USERS = [
61
- { id: "dev-russell", email: "russell@codedrifters.com" },
62
- { id: "dev-cameron", email: "cameron@codedrifters.com" },
63
- { id: "dev-neelima", email: "neelima@codedrifters.com" },
64
- { id: "dev-garon", email: "garon@codedrifters.com" },
65
- { id: "dev-dave", email: "dave@codedrifters.com" },
66
- { id: "dev-drew", email: "drew@codedrifters.com" },
67
- { id: "dev-jessica", email: "jessica@codedrifters.com" },
68
- { id: "dev-jared", email: "jared@codedrifters.com" },
69
- { id: "dev-goddess", email: "goddess@codedrifters.com" },
70
- // Dedicated end-to-end test principal for admin-console Playwright
71
- // specs (issue #1275). Reuses the standard DEV_USERS plumbing
72
- // (Cognito user + DynamoDB User + per-tenant Memberships +
73
- // tenant-admin RAs + platform-scoped system-admin RA) and the
74
- // existing SSM-sourced password convention from #1249 — the
75
- // operator provisions the SecureString at
76
- // /openhi/seed/users/e2e-admin-console_at_codedrifters.com/password
77
- // out of band, and the seeded Cognito user picks it up via
78
- // AdminSetUserPassword on every seed run.
79
- { id: "dev-e2e-admin-console", email: "e2e-admin-console@codedrifters.com" }
80
- ];
81
- var DEMO_TENANT_SPECS = [
82
- {
83
- scenario: "placeholder",
84
- tenantId: PLACEHOLDER_TENANT_ID,
85
- tenantName: "OpenHI Placeholder Tenant",
86
- workspaces: [
87
- {
88
- id: PLACEHOLDER_WORKSPACE_ID,
89
- name: "OpenHI Placeholder Workspace",
90
- roleSuffix: "workspace"
91
- }
92
- ]
93
- },
94
- {
95
- scenario: "demo-wound-care",
96
- tenantId: "demo-wound-care-tenant",
97
- tenantName: "Cedarbrook Wound Healing Institute",
98
- workspaces: [
99
- {
100
- id: "demo-wound-care-workspace",
101
- name: "Cedarbrook Outpatient Wound Clinic",
102
- roleSuffix: "workspace"
103
- }
104
- ]
105
- },
106
- {
107
- scenario: "demo-primary-care",
108
- tenantId: "demo-primary-care-tenant",
109
- tenantName: "Maple Ridge Family Medicine",
110
- workspaces: [
111
- {
112
- id: "demo-primary-care-workspace",
113
- name: "Maple Ridge Main Street Office",
114
- roleSuffix: "workspace"
115
- }
116
- ]
117
- },
118
- {
119
- scenario: "demo-mixed",
120
- tenantId: "demo-mixed-tenant",
121
- tenantName: "Northbridge Health Network",
122
- workspaces: [
123
- {
124
- id: "demo-mixed-workspace-wound-care",
125
- name: "Northbridge Wound Care Center",
126
- roleSuffix: "workspace-wound-care"
127
- },
128
- {
129
- id: "demo-mixed-workspace-primary-care",
130
- name: "Northbridge Family Practice",
131
- roleSuffix: "workspace-primary-care"
132
- }
133
- ]
134
- }
135
- ];
136
- var demoMembershipId = (devUserId, tenantId) => `demo-membership-${devUserId}-${tenantId}`;
137
- var demoRoleAssignmentId = (devUserId, tenantId, roleCode) => `demo-roleassignment-${devUserId}-${tenantId}-${roleCode}`;
138
- var demoScenarioIdentifier = (scenario, roleSuffix) => ({
139
- system: DEMO_URN_SYSTEM,
140
- value: `${scenario}:${roleSuffix}`
141
- });
142
- var openhiResourceIdentifier = (params) => ({
143
- use: "unversioned",
144
- system: OPENHI_RESOURCE_URN_SYSTEM,
145
- value: `urn:ohi:${params.tenantId}:${params.workspaceId}:${params.resourceType}:${params.id}`
146
- });
147
- var demoRolesForUserInTenant = (_user, _tenantId) => {
148
- void _user;
149
- void _tenantId;
150
- return [PLATFORM_ROLE_CODE.TENANT_ADMIN];
151
- };
152
-
153
- // src/workflows/control-plane/seed-demo-data/data-plane-fixtures.ts
154
- var fixtureIdentifiers = (scenario, tenantId, workspaceId, resourceType, id, roleSuffix) => [
155
- demoScenarioIdentifier(scenario, roleSuffix),
156
- openhiResourceIdentifier({
157
- tenantId,
158
- workspaceId,
159
- resourceType,
160
- id
161
- })
162
- ];
163
- var buildWoundCareFixtures = (scenario, tenantId, workspaceId, idPrefix) => ({
164
- tenantId,
165
- workspaceId,
166
- scenario,
167
- patients: [
168
- {
169
- resourceType: "Patient",
170
- id: `${idPrefix}-patient-1`,
171
- identifier: fixtureIdentifiers(
172
- scenario,
173
- tenantId,
174
- workspaceId,
175
- "Patient",
176
- `${idPrefix}-patient-1`,
177
- `patient-1`
178
- ),
179
- active: true,
180
- name: [{ family: "Carter", given: ["Eleanor"], use: "official" }],
181
- gender: "female",
182
- birthDate: "1952-04-18"
183
- },
184
- {
185
- resourceType: "Patient",
186
- id: `${idPrefix}-patient-2`,
187
- identifier: fixtureIdentifiers(
188
- scenario,
189
- tenantId,
190
- workspaceId,
191
- "Patient",
192
- `${idPrefix}-patient-2`,
193
- `patient-2`
194
- ),
195
- active: true,
196
- name: [{ family: "Nguyen", given: ["Hao"], use: "official" }],
197
- gender: "male",
198
- birthDate: "1968-11-02"
199
- }
200
- ],
201
- practitioners: [
202
- {
203
- resourceType: "Practitioner",
204
- id: `${idPrefix}-practitioner-1`,
205
- identifier: fixtureIdentifiers(
206
- scenario,
207
- tenantId,
208
- workspaceId,
209
- "Practitioner",
210
- `${idPrefix}-practitioner-1`,
211
- `practitioner-1`
212
- ),
213
- active: true,
214
- name: [{ family: "Reyes", given: ["Maria"], prefix: ["Dr."] }],
215
- gender: "female"
216
- },
217
- {
218
- resourceType: "Practitioner",
219
- id: `${idPrefix}-practitioner-2`,
220
- identifier: fixtureIdentifiers(
221
- scenario,
222
- tenantId,
223
- workspaceId,
224
- "Practitioner",
225
- `${idPrefix}-practitioner-2`,
226
- `practitioner-2`
227
- ),
228
- active: true,
229
- name: [{ family: "Okafor", given: ["Chinedu"], prefix: ["Dr."] }],
230
- gender: "male"
231
- }
232
- ],
233
- observations: [
234
- {
235
- resourceType: "Observation",
236
- id: `${idPrefix}-observation-1`,
237
- identifier: fixtureIdentifiers(
238
- scenario,
239
- tenantId,
240
- workspaceId,
241
- "Observation",
242
- `${idPrefix}-observation-1`,
243
- `observation-1`
244
- ),
245
- status: "final",
246
- code: {
247
- coding: [
248
- {
249
- system: "http://loinc.org",
250
- code: "39135-9",
251
- display: "Wound size"
252
- }
253
- ]
254
- },
255
- subject: { reference: `Patient/${idPrefix}-patient-1` },
256
- valueString: "3.2cm x 2.1cm"
257
- },
258
- {
259
- resourceType: "Observation",
260
- id: `${idPrefix}-observation-2`,
261
- identifier: fixtureIdentifiers(
262
- scenario,
263
- tenantId,
264
- workspaceId,
265
- "Observation",
266
- `${idPrefix}-observation-2`,
267
- `observation-2`
268
- ),
269
- status: "final",
270
- code: {
271
- coding: [
272
- {
273
- system: "http://loinc.org",
274
- code: "72287-2",
275
- display: "Wound exudate amount"
276
- }
277
- ]
278
- },
279
- subject: { reference: `Patient/${idPrefix}-patient-2` },
280
- valueString: "moderate"
281
- }
282
- ],
283
- encounters: [
284
- {
285
- resourceType: "Encounter",
286
- id: `${idPrefix}-encounter-1`,
287
- identifier: fixtureIdentifiers(
288
- scenario,
289
- tenantId,
290
- workspaceId,
291
- "Encounter",
292
- `${idPrefix}-encounter-1`,
293
- `encounter-1`
294
- ),
295
- status: "finished",
296
- class: {
297
- system: "http://terminology.hl7.org/CodeSystem/v3-ActCode",
298
- code: "AMB",
299
- display: "ambulatory"
300
- },
301
- subject: { reference: `Patient/${idPrefix}-patient-1` }
302
- },
303
- {
304
- resourceType: "Encounter",
305
- id: `${idPrefix}-encounter-2`,
306
- identifier: fixtureIdentifiers(
307
- scenario,
308
- tenantId,
309
- workspaceId,
310
- "Encounter",
311
- `${idPrefix}-encounter-2`,
312
- `encounter-2`
313
- ),
314
- status: "finished",
315
- class: {
316
- system: "http://terminology.hl7.org/CodeSystem/v3-ActCode",
317
- code: "AMB",
318
- display: "ambulatory"
319
- },
320
- subject: { reference: `Patient/${idPrefix}-patient-2` }
321
- }
322
- ],
323
- accounts: [
324
- {
325
- resourceType: "Account",
326
- id: `${idPrefix}-account-1`,
327
- identifier: fixtureIdentifiers(
328
- scenario,
329
- tenantId,
330
- workspaceId,
331
- "Account",
332
- `${idPrefix}-account-1`,
333
- `account-1`
334
- ),
335
- status: "active",
336
- name: "Wound-care self-pay account",
337
- subject: [{ reference: `Patient/${idPrefix}-patient-1` }]
338
- }
339
- ]
340
- });
341
- var buildPrimaryCareFixtures = (scenario, tenantId, workspaceId, idPrefix) => ({
342
- tenantId,
343
- workspaceId,
344
- scenario,
345
- patients: [
346
- {
347
- resourceType: "Patient",
348
- id: `${idPrefix}-patient-1`,
349
- identifier: fixtureIdentifiers(
350
- scenario,
351
- tenantId,
352
- workspaceId,
353
- "Patient",
354
- `${idPrefix}-patient-1`,
355
- `patient-1`
356
- ),
357
- active: true,
358
- name: [{ family: "Bennett", given: ["Sophia"], use: "official" }],
359
- gender: "female",
360
- birthDate: "1985-06-09"
361
- },
362
- {
363
- resourceType: "Patient",
364
- id: `${idPrefix}-patient-2`,
365
- identifier: fixtureIdentifiers(
366
- scenario,
367
- tenantId,
368
- workspaceId,
369
- "Patient",
370
- `${idPrefix}-patient-2`,
371
- `patient-2`
372
- ),
373
- active: true,
374
- name: [{ family: "Patel", given: ["Arjun"], use: "official" }],
375
- gender: "male",
376
- birthDate: "1979-02-21"
377
- }
378
- ],
379
- practitioners: [
380
- {
381
- resourceType: "Practitioner",
382
- id: `${idPrefix}-practitioner-1`,
383
- identifier: fixtureIdentifiers(
384
- scenario,
385
- tenantId,
386
- workspaceId,
387
- "Practitioner",
388
- `${idPrefix}-practitioner-1`,
389
- `practitioner-1`
390
- ),
391
- active: true,
392
- name: [{ family: "Lin", given: ["Wei"], prefix: ["Dr."] }],
393
- gender: "female"
394
- },
395
- {
396
- resourceType: "Practitioner",
397
- id: `${idPrefix}-practitioner-2`,
398
- identifier: fixtureIdentifiers(
399
- scenario,
400
- tenantId,
401
- workspaceId,
402
- "Practitioner",
403
- `${idPrefix}-practitioner-2`,
404
- `practitioner-2`
405
- ),
406
- active: true,
407
- name: [{ family: "Kowalski", given: ["Piotr"], prefix: ["Dr."] }],
408
- gender: "male"
409
- }
410
- ],
411
- observations: [
412
- {
413
- resourceType: "Observation",
414
- id: `${idPrefix}-observation-1`,
415
- identifier: fixtureIdentifiers(
416
- scenario,
417
- tenantId,
418
- workspaceId,
419
- "Observation",
420
- `${idPrefix}-observation-1`,
421
- `observation-1`
422
- ),
423
- status: "final",
424
- code: {
425
- coding: [
426
- {
427
- system: "http://loinc.org",
428
- code: "8480-6",
429
- display: "Systolic blood pressure"
430
- }
431
- ]
432
- },
433
- subject: { reference: `Patient/${idPrefix}-patient-1` },
434
- valueQuantity: { value: 122, unit: "mm[Hg]" }
435
- },
436
- {
437
- resourceType: "Observation",
438
- id: `${idPrefix}-observation-2`,
439
- identifier: fixtureIdentifiers(
440
- scenario,
441
- tenantId,
442
- workspaceId,
443
- "Observation",
444
- `${idPrefix}-observation-2`,
445
- `observation-2`
446
- ),
447
- status: "final",
448
- code: {
449
- coding: [
450
- {
451
- system: "http://loinc.org",
452
- code: "8462-4",
453
- display: "Diastolic blood pressure"
454
- }
455
- ]
456
- },
457
- subject: { reference: `Patient/${idPrefix}-patient-2` },
458
- valueQuantity: { value: 78, unit: "mm[Hg]" }
459
- }
460
- ],
461
- encounters: [
462
- {
463
- resourceType: "Encounter",
464
- id: `${idPrefix}-encounter-1`,
465
- identifier: fixtureIdentifiers(
466
- scenario,
467
- tenantId,
468
- workspaceId,
469
- "Encounter",
470
- `${idPrefix}-encounter-1`,
471
- `encounter-1`
472
- ),
473
- status: "finished",
474
- class: {
475
- system: "http://terminology.hl7.org/CodeSystem/v3-ActCode",
476
- code: "AMB",
477
- display: "ambulatory"
478
- },
479
- subject: { reference: `Patient/${idPrefix}-patient-1` }
480
- },
481
- {
482
- resourceType: "Encounter",
483
- id: `${idPrefix}-encounter-2`,
484
- identifier: fixtureIdentifiers(
485
- scenario,
486
- tenantId,
487
- workspaceId,
488
- "Encounter",
489
- `${idPrefix}-encounter-2`,
490
- `encounter-2`
491
- ),
492
- status: "in-progress",
493
- class: {
494
- system: "http://terminology.hl7.org/CodeSystem/v3-ActCode",
495
- code: "AMB",
496
- display: "ambulatory"
497
- },
498
- subject: { reference: `Patient/${idPrefix}-patient-2` }
499
- }
500
- ],
501
- accounts: [
502
- {
503
- resourceType: "Account",
504
- id: `${idPrefix}-account-1`,
505
- identifier: fixtureIdentifiers(
506
- scenario,
507
- tenantId,
508
- workspaceId,
509
- "Account",
510
- `${idPrefix}-account-1`,
511
- `account-1`
512
- ),
513
- status: "active",
514
- name: "Primary-care insurance account",
515
- subject: [{ reference: `Patient/${idPrefix}-patient-1` }]
516
- }
517
- ]
518
- });
519
- var DEMO_DATA_PLANE_FIXTURES = [
520
- buildWoundCareFixtures(
521
- "demo-wound-care",
522
- "demo-wound-care-tenant",
523
- "demo-wound-care-workspace",
524
- "demo-wound-care"
525
- ),
526
- buildPrimaryCareFixtures(
527
- "demo-primary-care",
528
- "demo-primary-care-tenant",
529
- "demo-primary-care-workspace",
530
- "demo-primary-care"
531
- ),
532
- buildWoundCareFixtures(
533
- "demo-mixed",
534
- "demo-mixed-tenant",
535
- "demo-mixed-workspace-wound-care",
536
- "demo-mixed-wound-care"
537
- ),
538
- buildPrimaryCareFixtures(
539
- "demo-mixed",
540
- "demo-mixed-tenant",
541
- "demo-mixed-workspace-primary-care",
542
- "demo-mixed-primary-care"
543
- )
544
- ];
545
- var _validateFixturesAgainstTenantSpecs = () => {
546
- for (const group of DEMO_DATA_PLANE_FIXTURES) {
547
- if (group.tenantId === PLACEHOLDER_TENANT_ID) {
548
- throw new Error(
549
- "The placeholder tenant must not carry data-plane fixtures."
550
- );
551
- }
552
- const tenant = DEMO_TENANT_SPECS.find((s) => s.tenantId === group.tenantId);
553
- if (!tenant) {
554
- throw new Error(
555
- `Fixture references unknown tenantId "${group.tenantId}". Add a matching entry to DEMO_TENANT_SPECS first.`
556
- );
557
- }
558
- const workspace = tenant.workspaces.find(
559
- (ws) => ws.id === group.workspaceId
560
- );
561
- if (!workspace) {
562
- throw new Error(
563
- `Fixture references unknown workspaceId "${group.workspaceId}" for tenant "${group.tenantId}".`
564
- );
565
- }
566
- }
567
- };
568
- _validateFixturesAgainstTenantSpecs();
569
-
570
- // src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts
571
- var SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR = "SEED_DEMO_DATA_USER_POOL_ID";
572
- var errorMessage = (err) => {
573
- if (err instanceof Error) {
574
- return err.message;
575
- }
576
- return String(err);
577
- };
578
- var tryRun = async (failures, phase, scope, resourceType, resourceId, fn) => {
579
- try {
580
- await fn();
581
- return true;
582
- } catch (err) {
583
- failures.push({ phase, scope, resourceType, resourceId, error: err });
584
- return false;
585
- }
586
- };
587
- var aggregateFailureError = (failures) => {
588
- const summary = failures.map(
589
- (f) => `${f.phase} ${f.scope}/${f.resourceType}/${f.resourceId}: ${errorMessage(
590
- f.error
591
- )}`
592
- ).join("; ");
593
- return new Error(
594
- `seed-demo-data: ${failures.length} item(s) failed across phases: ${summary}`
595
- );
596
- };
597
- var idForRoleCode = (code) => {
598
- for (const key of Object.keys(PLATFORM_ROLE_IDS)) {
599
- if (PLATFORM_ROLE_CONCEPTS[key].code === code) {
600
- return PLATFORM_ROLE_IDS[key];
601
- }
602
- }
603
- throw new Error(`No id mapping for role code "${code}".`);
604
- };
605
- var verifySystemRolesExist = async () => {
606
- const probeContext = {
607
- tenantId: "",
608
- workspaceId: "",
609
- date: (/* @__PURE__ */ new Date(0)).toISOString(),
610
- actorId: "platform-deploy-bridge",
611
- actorName: "Platform Deploy Bridge",
612
- actorType: "internal-system",
613
- source: "step-function"
614
- };
615
- for (const id of Object.values(PLATFORM_ROLE_IDS)) {
616
- try {
617
- await getRoleByIdOperation({ context: probeContext, id });
618
- } catch (err) {
619
- if (err instanceof NotFoundError) {
620
- throw new Error(
621
- `seed-demo-data pre-flight: control-plane Role "${id}" is missing. Ensure seed-system-data has run on this environment before retrying.`
622
- );
623
- }
624
- throw err;
625
- }
626
- }
627
- };
628
- var tenantResourceBody = (spec) => ({
629
- name: spec.tenantName,
630
- active: true,
631
- identifier: [
632
- demoScenarioIdentifier(spec.scenario, "tenant"),
633
- openhiResourceIdentifier({
634
- tenantId: spec.tenantId,
635
- workspaceId: "",
636
- resourceType: "Tenant",
637
- id: spec.tenantId
638
- })
639
- ]
640
- });
641
- var workspaceResourceBody = (spec, workspace) => ({
642
- name: workspace.name,
643
- active: true,
644
- identifier: [
645
- demoScenarioIdentifier(spec.scenario, workspace.roleSuffix),
646
- openhiResourceIdentifier({
647
- tenantId: spec.tenantId,
648
- workspaceId: "",
649
- resourceType: "Workspace",
650
- id: workspace.id
651
- })
652
- ],
653
- tenant: { reference: `Tenant/${spec.tenantId}`, type: "Tenant" }
654
- });
655
- var membershipResourceBody = (spec, user, membershipId) => ({
656
- identifier: [
657
- demoScenarioIdentifier(spec.scenario, `membership-${user.id}`),
658
- openhiResourceIdentifier({
659
- tenantId: spec.tenantId,
660
- workspaceId: "",
661
- resourceType: "Membership",
662
- id: membershipId
663
- })
664
- ],
665
- user: { reference: `User/${user.id}`, type: "User" },
666
- tenant: { reference: `Tenant/${spec.tenantId}`, type: "Tenant" },
667
- period: DEMO_PERIOD
668
- });
669
- var roleAssignmentResourceBody = (scenario, tenantId, user, roleCode, roleAssignmentId) => ({
670
- identifier: [
671
- demoScenarioIdentifier(scenario, `roleassignment-${user.id}-${roleCode}`),
672
- openhiResourceIdentifier({
673
- tenantId,
674
- workspaceId: "",
675
- resourceType: "RoleAssignment",
676
- id: roleAssignmentId
677
- })
678
- ],
679
- user: { reference: `User/${user.id}`, type: "User" },
680
- role: { reference: `Role/${idForRoleCode(roleCode)}`, type: "Role" },
681
- tenant: { reference: `Tenant/${tenantId}`, type: "Tenant" },
682
- period: DEMO_PERIOD
683
- });
684
- var userResourceBody = (user, cognitoSub) => ({
685
- resourceType: "User",
686
- id: user.id,
687
- name: [{ text: user.email }],
688
- status: "active",
689
- cognitoSub,
690
- currentTenant: { reference: `Tenant/${PLACEHOLDER_TENANT_ID}` },
691
- currentWorkspace: { reference: `Workspace/${PLACEHOLDER_WORKSPACE_ID}` }
692
- });
693
- var upsertUser = async (context, user, cognitoSub) => {
694
- const service = getDynamoControlService();
695
- const resource = userResourceBody(user, cognitoSub);
696
- const summary = JSON.stringify(extractSummary(resource));
697
- await service.entities.user.put({
698
- id: user.id,
699
- cognitoSub,
700
- resource: JSON.stringify(resource),
701
- summary,
702
- vid: "1",
703
- lastUpdated: context.date ?? (/* @__PURE__ */ new Date()).toISOString()
704
- }).go();
705
- };
706
- var seedWorkspaceDataPlane = async (baseContext, group, failures) => {
707
- const workspaceContext = {
708
- ...baseContext,
709
- tenantId: group.tenantId,
710
- workspaceId: group.workspaceId
711
- };
712
- const scope = `${group.tenantId}/${group.workspaceId}`;
713
- for (const patient of group.patients) {
714
- await tryRun(
715
- failures,
716
- "phase-3",
717
- scope,
718
- "Patient",
719
- patient.id ?? "",
720
- () => createPatientOperation({
721
- context: workspaceContext,
722
- body: patient
723
- })
724
- );
725
- }
726
- for (const practitioner of group.practitioners) {
727
- await tryRun(
728
- failures,
729
- "phase-3",
730
- scope,
731
- "Practitioner",
732
- practitioner.id ?? "",
733
- () => createPractitionerOperation({
734
- context: workspaceContext,
735
- body: practitioner
736
- })
737
- );
738
- }
739
- for (const observation of group.observations) {
740
- await tryRun(
741
- failures,
742
- "phase-3",
743
- scope,
744
- "Observation",
745
- observation.id ?? "",
746
- () => createObservationOperation({
747
- context: workspaceContext,
748
- body: observation
749
- })
750
- );
751
- }
752
- for (const encounter of group.encounters) {
753
- await tryRun(
754
- failures,
755
- "phase-3",
756
- scope,
757
- "Encounter",
758
- encounter.id ?? "",
759
- () => createEncounterOperation({
760
- context: workspaceContext,
761
- body: encounter
762
- })
763
- );
764
- }
765
- for (const account of group.accounts) {
766
- await tryRun(
767
- failures,
768
- "phase-3",
769
- scope,
770
- "Account",
771
- account.id ?? "",
772
- () => createAccountOperation({
773
- context: workspaceContext,
774
- body: account
775
- })
776
- );
777
- }
778
- };
779
- var seedDemoGraph = async (params) => {
780
- const { baseContext, devUsers, cognito } = params;
781
- const failures = [];
782
- for (const spec of DEMO_TENANT_SPECS) {
783
- const tenantContext = {
784
- ...baseContext,
785
- tenantId: spec.tenantId
786
- };
787
- await tryRun(
788
- failures,
789
- "phase-1",
790
- spec.tenantId,
791
- "Tenant",
792
- spec.tenantId,
793
- () => createTenantOperation({
794
- context: tenantContext,
795
- body: { id: spec.tenantId, resource: tenantResourceBody(spec) }
796
- })
797
- );
798
- for (const workspace of spec.workspaces) {
799
- await tryRun(
800
- failures,
801
- "phase-1",
802
- spec.tenantId,
803
- "Workspace",
804
- workspace.id,
805
- () => createWorkspaceOperation({
806
- context: tenantContext,
807
- body: {
808
- id: workspace.id,
809
- resource: workspaceResourceBody(spec, workspace)
810
- }
811
- })
812
- );
813
- }
814
- }
815
- for (const user of devUsers) {
816
- let cognitoSub;
817
- try {
818
- cognitoSub = await cognito.ensureUser(user.email);
819
- } catch (err) {
820
- failures.push({
821
- phase: "phase-2",
822
- scope: user.id,
823
- resourceType: "CognitoUser",
824
- resourceId: user.email,
825
- error: err
826
- });
827
- continue;
828
- }
829
- await tryRun(
830
- failures,
831
- "phase-2",
832
- user.id,
833
- "User",
834
- user.id,
835
- () => upsertUser(baseContext, user, cognitoSub)
836
- );
837
- for (const spec of DEMO_TENANT_SPECS) {
838
- const tenantContext = {
839
- ...baseContext,
840
- tenantId: spec.tenantId
841
- };
842
- const userScope = `${user.id}@${spec.tenantId}`;
843
- const membershipId = demoMembershipId(user.id, spec.tenantId);
844
- await tryRun(
845
- failures,
846
- "phase-2",
847
- userScope,
848
- "Membership",
849
- membershipId,
850
- () => createMembershipOperation({
851
- context: tenantContext,
852
- body: {
853
- id: membershipId,
854
- resource: membershipResourceBody(spec, user, membershipId)
855
- }
856
- })
857
- );
858
- for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {
859
- const raId = demoRoleAssignmentId(user.id, spec.tenantId, roleCode);
860
- await tryRun(
861
- failures,
862
- "phase-2",
863
- userScope,
864
- "RoleAssignment",
865
- raId,
866
- () => createRoleAssignmentOperation({
867
- context: tenantContext,
868
- body: {
869
- id: raId,
870
- resource: roleAssignmentResourceBody(
871
- spec.scenario,
872
- spec.tenantId,
873
- user,
874
- roleCode,
875
- raId
876
- )
877
- }
878
- })
879
- );
880
- }
881
- }
882
- const platformContext = {
883
- ...baseContext,
884
- tenantId: PLATFORM_SCOPE_TENANT_ID
885
- };
886
- const platformRoleCode = PLATFORM_ROLE_CODE2.SYSTEM_ADMIN;
887
- const platformRaId = demoRoleAssignmentId(
888
- user.id,
889
- PLATFORM_SCOPE_TENANT_ID,
890
- platformRoleCode
891
- );
892
- await tryRun(
893
- failures,
894
- "phase-2",
895
- `${user.id}@${PLATFORM_SCOPE_TENANT_ID}`,
896
- "RoleAssignment",
897
- platformRaId,
898
- () => createRoleAssignmentOperation({
899
- context: platformContext,
900
- body: {
901
- id: platformRaId,
902
- resource: roleAssignmentResourceBody(
903
- "platform",
904
- PLATFORM_SCOPE_TENANT_ID,
905
- user,
906
- platformRoleCode,
907
- platformRaId
908
- )
909
- }
910
- })
911
- );
912
- }
913
- for (const group of DEMO_DATA_PLANE_FIXTURES) {
914
- try {
915
- await seedWorkspaceDataPlane(baseContext, group, failures);
916
- } catch (err) {
917
- failures.push({
918
- phase: "phase-3",
919
- scope: `${group.tenantId}/${group.workspaceId}`,
920
- resourceType: "Workspace",
921
- resourceId: group.workspaceId,
922
- error: err
923
- });
924
- }
925
- }
926
- if (failures.length > 0) {
927
- throw aggregateFailureError(failures);
928
- }
929
- };
930
- var runSeedDemoData = async (event, deps, devUsers) => {
931
- const parsed = (0, import_workflows2.parseWorkflowEvent)(event, import_workflows.PlatformSystemDataSeededV1);
932
- const recordResult = await deps.dedupClient.recordIfAbsent({
933
- consumerName: SEED_DEMO_DATA_CONSUMER_NAME,
934
- eventId: parsed.dedupKey.eventId,
935
- attempt: parsed.dedupKey.attempt
936
- });
937
- if (!recordResult.recorded) {
938
- return;
939
- }
940
- const baseContext = {
941
- tenantId: "",
942
- workspaceId: "",
943
- date: parsed.envelope.occurredAt,
944
- actorId: "platform-deploy-bridge",
945
- actorName: "Platform Deploy Bridge",
946
- actorType: "internal-system",
947
- source: "step-function"
948
- };
949
- try {
950
- await deps.verifyRoles();
951
- await deps.seedDemoGraph({
952
- baseContext,
953
- devUsers,
954
- cognito: deps.cognito
955
- });
956
- } catch (err) {
957
- await deps.dedupClient.markFailed({
958
- consumerName: SEED_DEMO_DATA_CONSUMER_NAME,
959
- eventId: parsed.dedupKey.eventId,
960
- attempt: parsed.dedupKey.attempt,
961
- reason: errorMessage(err)
962
- });
963
- throw err;
964
- }
965
- };
966
- var SEED_USER_PASSWORD_PARAMETER_PREFIX = "/openhi/seed/users/";
967
- var SSM_PATH_SEGMENT = /^[A-Za-z0-9_.-]+$/;
968
- var emailToSsmPath = (email) => {
969
- if (typeof email !== "string" || email.length === 0) {
970
- throw new Error(
971
- `emailToSsmPath: email must be a non-empty string (received "${String(email)}").`
972
- );
973
- }
974
- const atIdx = email.indexOf("@");
975
- if (atIdx === -1 || atIdx !== email.lastIndexOf("@")) {
976
- throw new Error(
977
- `emailToSsmPath: email "${email}" must contain exactly one "@" character.`
978
- );
979
- }
980
- const localPart = email.slice(0, atIdx);
981
- const domainPart = email.slice(atIdx + 1);
982
- if (localPart.length === 0 || domainPart.length === 0) {
983
- throw new Error(
984
- `emailToSsmPath: email "${email}" must have a non-empty local-part and domain.`
985
- );
986
- }
987
- if (!SSM_PATH_SEGMENT.test(localPart) || !SSM_PATH_SEGMENT.test(domainPart)) {
988
- throw new Error(
989
- `emailToSsmPath: email "${email}" contains characters that would produce an invalid SSM parameter path (only A-Z, a-z, 0-9, '.', '-', and '_' are allowed).`
990
- );
991
- }
992
- return `${SEED_USER_PASSWORD_PARAMETER_PREFIX}${localPart}_at_${domainPart}/password`;
993
- };
994
- var cachedSsmClient;
995
- var getSsmClient = () => {
996
- if (!cachedSsmClient) {
997
- cachedSsmClient = new SSMClient({});
998
- }
999
- return cachedSsmClient;
1000
- };
1001
- var __resetSsmClientForTests = () => {
1002
- cachedSsmClient = void 0;
1003
- };
1004
- var fetchSeedUserPassword = async (email) => {
1005
- const path = emailToSsmPath(email);
1006
- const client = getSsmClient();
1007
- try {
1008
- const result = await client.send(
1009
- new GetParameterCommand({ Name: path, WithDecryption: true })
1010
- );
1011
- const value = result.Parameter?.Value;
1012
- if (typeof value !== "string" || value.length === 0) {
1013
- throw new Error(
1014
- `fetchSeedUserPassword: SSM parameter "${path}" returned an empty value.`
1015
- );
1016
- }
1017
- return value;
1018
- } catch (err) {
1019
- if (err instanceof ParameterNotFound) {
1020
- throw new Error(
1021
- `fetchSeedUserPassword: SSM parameter "${path}" not found. Provision a SecureString at "${path}" with the dev user's password before re-running seed-demo-data.`
1022
- );
1023
- }
1024
- throw err;
1025
- }
1026
- };
1027
- var productionCognitoProvisioner = () => {
1028
- const client = new CognitoIdentityProviderClient({});
1029
- const userPoolId = process.env[SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR];
1030
- if (!userPoolId || userPoolId.trim() === "") {
1031
- throw new Error(
1032
- `${SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR} is not set; the seed-demo-data Lambda cannot provision Cognito users without a user-pool id.`
1033
- );
1034
- }
1035
- const subFromAttributes = (attrs) => {
1036
- for (const attr of attrs ?? []) {
1037
- if (attr.Name === "sub" && typeof attr.Value === "string") {
1038
- return attr.Value;
1039
- }
1040
- }
1041
- return void 0;
1042
- };
1043
- const setPassword = async (email, password) => {
1044
- await client.send(
1045
- new AdminSetUserPasswordCommand({
1046
- UserPoolId: userPoolId,
1047
- Username: email,
1048
- Password: password,
1049
- Permanent: true
1050
- })
1051
- );
1052
- };
1053
- return {
1054
- ensureUser: async (email) => {
1055
- const password = await fetchSeedUserPassword(email);
1056
- try {
1057
- const created = await client.send(
1058
- new AdminCreateUserCommand({
1059
- UserPoolId: userPoolId,
1060
- Username: email,
1061
- MessageAction: "SUPPRESS",
1062
- UserAttributes: [
1063
- { Name: "email", Value: email },
1064
- { Name: "email_verified", Value: "true" }
1065
- ]
1066
- })
1067
- );
1068
- await setPassword(email, password);
1069
- const sub = subFromAttributes(created.User?.Attributes);
1070
- if (!sub) {
1071
- throw new Error(
1072
- `AdminCreateUser response for "${email}" did not carry a sub attribute.`
1073
- );
1074
- }
1075
- return sub;
1076
- } catch (err) {
1077
- if (err instanceof UsernameExistsException) {
1078
- await setPassword(email, password);
1079
- const got = await client.send(
1080
- new AdminGetUserCommand({
1081
- UserPoolId: userPoolId,
1082
- Username: email
1083
- })
1084
- );
1085
- const sub = subFromAttributes(got.UserAttributes);
1086
- if (!sub) {
1087
- throw new Error(
1088
- `AdminGetUser response for "${email}" did not carry a sub attribute.`
1089
- );
1090
- }
1091
- return sub;
1092
- }
1093
- throw err;
1094
- }
1095
- }
1096
- };
1097
- };
1098
- var productionDependencies = () => {
1099
- const dynamodb = new DynamoDBClient({});
1100
- const cognito = productionCognitoProvisioner();
1101
- return {
1102
- dedupClient: (0, import_workflows2.workflowDedupClient)(dynamodb),
1103
- verifyRoles: verifySystemRolesExist,
1104
- seedDemoGraph,
1105
- cognito
1106
- };
1107
- };
1108
- var handler = async (event) => runSeedDemoData(event, productionDependencies(), DEV_USERS);
1109
-
1110
- export {
1111
- import_workflows,
1112
- SEED_DEMO_DATA_CONSUMER_NAME,
1113
- DEMO_URN_SYSTEM,
1114
- OPENHI_RESOURCE_URN_SYSTEM,
1115
- DEMO_PERIOD,
1116
- PLACEHOLDER_TENANT_ID,
1117
- PLACEHOLDER_WORKSPACE_ID,
1118
- DEV_USERS,
1119
- DEMO_TENANT_SPECS,
1120
- demoMembershipId,
1121
- demoRoleAssignmentId,
1122
- demoScenarioIdentifier,
1123
- openhiResourceIdentifier,
1124
- demoRolesForUserInTenant,
1125
- DEMO_DATA_PLANE_FIXTURES,
1126
- SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR,
1127
- seedDemoGraph,
1128
- runSeedDemoData,
1129
- SEED_USER_PASSWORD_PARAMETER_PREFIX,
1130
- emailToSsmPath,
1131
- __resetSsmClientForTests,
1132
- fetchSeedUserPassword,
1133
- productionCognitoProvisioner,
1134
- handler
1135
- };
1136
- //# sourceMappingURL=chunk-BQMJSDOD.mjs.map