@openhi/constructs 0.0.85 → 0.0.86
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-SWSN6GDD.mjs → chunk-CEOAGPYY.mjs} +1 -5
- package/lib/chunk-CEOAGPYY.mjs.map +1 -0
- package/lib/chunk-X5MHU7DA.mjs +298 -0
- package/lib/chunk-X5MHU7DA.mjs.map +1 -0
- package/lib/data-store-postgres-replication.handler.d.mts +55 -0
- package/lib/data-store-postgres-replication.handler.d.ts +55 -0
- package/lib/data-store-postgres-replication.handler.js +448 -0
- package/lib/data-store-postgres-replication.handler.js.map +1 -0
- package/lib/data-store-postgres-replication.handler.mjs +313 -0
- package/lib/data-store-postgres-replication.handler.mjs.map +1 -0
- package/lib/firehose-archive-transform.handler.js +0 -4
- package/lib/firehose-archive-transform.handler.js.map +1 -1
- package/lib/firehose-archive-transform.handler.mjs +5 -290
- package/lib/firehose-archive-transform.handler.mjs.map +1 -1
- package/lib/index.d.mts +201 -5
- package/lib/index.d.ts +202 -6
- package/lib/index.js +392 -92
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +381 -81
- package/lib/index.mjs.map +1 -1
- package/lib/rest-api-lambda.handler.js +636 -153
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +639 -153
- package/lib/rest-api-lambda.handler.mjs.map +1 -1
- package/package.json +21 -13
- package/scripts/generate-operations.js +2 -2
- package/scripts/generate-routes.js +1 -1
- package/lib/chunk-SWSN6GDD.mjs.map +0 -1
|
@@ -172,6 +172,31 @@ var dynamoClient = new import_client_dynamodb.DynamoDBClient({
|
|
|
172
172
|
|
|
173
173
|
// src/data/dynamo/entities/control/configuration-entity.ts
|
|
174
174
|
var import_electrodb = require("electrodb");
|
|
175
|
+
|
|
176
|
+
// src/data/dynamo/shard.ts
|
|
177
|
+
var SHARD_COUNT = 4;
|
|
178
|
+
function computeShard(id) {
|
|
179
|
+
let hash = 2166136261;
|
|
180
|
+
for (let i = 0; i < id.length; i++) {
|
|
181
|
+
hash ^= id.charCodeAt(i);
|
|
182
|
+
hash = Math.imul(hash, 16777619);
|
|
183
|
+
}
|
|
184
|
+
return (hash >>> 0) % SHARD_COUNT;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/data/dynamo/entities/control/control-entity-common.ts
|
|
188
|
+
var gsi1ShardAttribute = {
|
|
189
|
+
type: "string",
|
|
190
|
+
watch: ["id"],
|
|
191
|
+
set: (_val, item) => {
|
|
192
|
+
if (typeof item?.id !== "string" || item.id.length === 0) {
|
|
193
|
+
return void 0;
|
|
194
|
+
}
|
|
195
|
+
return String(computeShard(item.id));
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/data/dynamo/entities/control/configuration-entity.ts
|
|
175
200
|
var ConfigurationEntity = new import_electrodb.Entity({
|
|
176
201
|
model: {
|
|
177
202
|
entity: "configuration",
|
|
@@ -224,6 +249,14 @@ var ConfigurationEntity = new import_electrodb.Entity({
|
|
|
224
249
|
type: "string",
|
|
225
250
|
required: true
|
|
226
251
|
},
|
|
252
|
+
/**
|
|
253
|
+
* Summary projection (key display fields as JSON string: id, key, status).
|
|
254
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
255
|
+
*/
|
|
256
|
+
summary: {
|
|
257
|
+
type: "string",
|
|
258
|
+
required: true
|
|
259
|
+
},
|
|
227
260
|
/** Version id (e.g. ULID). Tracks current version; S3 history key. */
|
|
228
261
|
vid: {
|
|
229
262
|
type: "string",
|
|
@@ -233,6 +266,7 @@ var ConfigurationEntity = new import_electrodb.Entity({
|
|
|
233
266
|
type: "string",
|
|
234
267
|
required: true
|
|
235
268
|
},
|
|
269
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
236
270
|
deleted: {
|
|
237
271
|
type: "boolean",
|
|
238
272
|
required: false
|
|
@@ -260,19 +294,27 @@ var ConfigurationEntity = new import_electrodb.Entity({
|
|
|
260
294
|
template: "KEY#${key}#SK#${sk}"
|
|
261
295
|
}
|
|
262
296
|
},
|
|
263
|
-
/**
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
297
|
+
/**
|
|
298
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Configuration entries for a
|
|
299
|
+
* (tenant, workspace) across the four shards. Use for "list configs scoped to this tenant"
|
|
300
|
+
* (workspaceId = "-") or "list configs scoped to this workspace". Does not support
|
|
301
|
+
* hierarchical resolution in one query; use base table GetItem in fallback order
|
|
302
|
+
* (user → workspace → tenant → baseline) for that.
|
|
303
|
+
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
304
|
+
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
305
|
+
*/
|
|
306
|
+
gsi1: {
|
|
307
|
+
index: "GSI1",
|
|
267
308
|
pk: {
|
|
268
|
-
field: "
|
|
269
|
-
composite: ["tenantId", "workspaceId"],
|
|
270
|
-
template: "TID#${tenantId}#WID#${workspaceId}#RT#Configuration"
|
|
309
|
+
field: "GSI1PK",
|
|
310
|
+
composite: ["tenantId", "workspaceId", "gsi1Shard"],
|
|
311
|
+
template: "TID#${tenantId}#WID#${workspaceId}#RT#Configuration#SHARD#${gsi1Shard}"
|
|
271
312
|
},
|
|
272
313
|
sk: {
|
|
273
|
-
field: "
|
|
274
|
-
|
|
275
|
-
|
|
314
|
+
field: "GSI1SK",
|
|
315
|
+
casing: "none",
|
|
316
|
+
composite: ["lastUpdated", "id"],
|
|
317
|
+
template: "${lastUpdated}#${id}"
|
|
276
318
|
}
|
|
277
319
|
}
|
|
278
320
|
}
|
|
@@ -308,6 +350,14 @@ var MembershipEntity = new import_electrodb2.Entity({
|
|
|
308
350
|
type: "string",
|
|
309
351
|
required: true
|
|
310
352
|
},
|
|
353
|
+
/**
|
|
354
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
355
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
356
|
+
*/
|
|
357
|
+
summary: {
|
|
358
|
+
type: "string",
|
|
359
|
+
required: true
|
|
360
|
+
},
|
|
311
361
|
/** Version id (e.g. ULID). */
|
|
312
362
|
vid: {
|
|
313
363
|
type: "string",
|
|
@@ -317,6 +367,7 @@ var MembershipEntity = new import_electrodb2.Entity({
|
|
|
317
367
|
type: "string",
|
|
318
368
|
required: true
|
|
319
369
|
},
|
|
370
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
320
371
|
deleted: {
|
|
321
372
|
type: "boolean",
|
|
322
373
|
required: false
|
|
@@ -344,19 +395,24 @@ var MembershipEntity = new import_electrodb2.Entity({
|
|
|
344
395
|
template: "${sk}"
|
|
345
396
|
}
|
|
346
397
|
},
|
|
347
|
-
/**
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
398
|
+
/**
|
|
399
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the
|
|
400
|
+
* four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.
|
|
401
|
+
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
402
|
+
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
403
|
+
*/
|
|
404
|
+
gsi1: {
|
|
405
|
+
index: "GSI1",
|
|
351
406
|
pk: {
|
|
352
|
-
field: "
|
|
353
|
-
composite: ["tenantId"],
|
|
354
|
-
template: "TID#${tenantId}#RT#Membership"
|
|
407
|
+
field: "GSI1PK",
|
|
408
|
+
composite: ["tenantId", "gsi1Shard"],
|
|
409
|
+
template: "TID#${tenantId}#WID#-#RT#Membership#SHARD#${gsi1Shard}"
|
|
355
410
|
},
|
|
356
411
|
sk: {
|
|
357
|
-
field: "
|
|
358
|
-
|
|
359
|
-
|
|
412
|
+
field: "GSI1SK",
|
|
413
|
+
casing: "none",
|
|
414
|
+
composite: ["lastUpdated", "id"],
|
|
415
|
+
template: "${lastUpdated}#${id}"
|
|
360
416
|
}
|
|
361
417
|
}
|
|
362
418
|
}
|
|
@@ -387,6 +443,14 @@ var RoleEntity = new import_electrodb3.Entity({
|
|
|
387
443
|
type: "string",
|
|
388
444
|
required: true
|
|
389
445
|
},
|
|
446
|
+
/**
|
|
447
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
448
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
449
|
+
*/
|
|
450
|
+
summary: {
|
|
451
|
+
type: "string",
|
|
452
|
+
required: true
|
|
453
|
+
},
|
|
390
454
|
/** Version id (e.g. ULID). */
|
|
391
455
|
vid: {
|
|
392
456
|
type: "string",
|
|
@@ -396,6 +460,7 @@ var RoleEntity = new import_electrodb3.Entity({
|
|
|
396
460
|
type: "string",
|
|
397
461
|
required: true
|
|
398
462
|
},
|
|
463
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
399
464
|
deleted: {
|
|
400
465
|
type: "boolean",
|
|
401
466
|
required: false
|
|
@@ -423,19 +488,24 @@ var RoleEntity = new import_electrodb3.Entity({
|
|
|
423
488
|
template: "${sk}"
|
|
424
489
|
}
|
|
425
490
|
},
|
|
426
|
-
/**
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
491
|
+
/**
|
|
492
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.
|
|
493
|
+
* Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.
|
|
494
|
+
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
495
|
+
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
496
|
+
*/
|
|
497
|
+
gsi1: {
|
|
498
|
+
index: "GSI1",
|
|
430
499
|
pk: {
|
|
431
|
-
field: "
|
|
432
|
-
composite: [],
|
|
433
|
-
template: "RT#Role"
|
|
500
|
+
field: "GSI1PK",
|
|
501
|
+
composite: ["gsi1Shard"],
|
|
502
|
+
template: "TID#-#WID#-#RT#Role#SHARD#${gsi1Shard}"
|
|
434
503
|
},
|
|
435
504
|
sk: {
|
|
436
|
-
field: "
|
|
437
|
-
|
|
438
|
-
|
|
505
|
+
field: "GSI1SK",
|
|
506
|
+
casing: "none",
|
|
507
|
+
composite: ["lastUpdated", "id"],
|
|
508
|
+
template: "${lastUpdated}#${id}"
|
|
439
509
|
}
|
|
440
510
|
}
|
|
441
511
|
}
|
|
@@ -471,6 +541,14 @@ var RoleAssignmentEntity = new import_electrodb4.Entity({
|
|
|
471
541
|
type: "string",
|
|
472
542
|
required: true
|
|
473
543
|
},
|
|
544
|
+
/**
|
|
545
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
546
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
547
|
+
*/
|
|
548
|
+
summary: {
|
|
549
|
+
type: "string",
|
|
550
|
+
required: true
|
|
551
|
+
},
|
|
474
552
|
/** Version id (e.g. ULID). */
|
|
475
553
|
vid: {
|
|
476
554
|
type: "string",
|
|
@@ -480,6 +558,7 @@ var RoleAssignmentEntity = new import_electrodb4.Entity({
|
|
|
480
558
|
type: "string",
|
|
481
559
|
required: true
|
|
482
560
|
},
|
|
561
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
483
562
|
deleted: {
|
|
484
563
|
type: "boolean",
|
|
485
564
|
required: false
|
|
@@ -507,19 +586,24 @@ var RoleAssignmentEntity = new import_electrodb4.Entity({
|
|
|
507
586
|
template: "${sk}"
|
|
508
587
|
}
|
|
509
588
|
},
|
|
510
|
-
/**
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
589
|
+
/**
|
|
590
|
+
* GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the
|
|
591
|
+
* four shards. Tenant-scoped only, so `WID#-` is a sentinel.
|
|
592
|
+
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
593
|
+
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
594
|
+
*/
|
|
595
|
+
gsi1: {
|
|
596
|
+
index: "GSI1",
|
|
514
597
|
pk: {
|
|
515
|
-
field: "
|
|
516
|
-
composite: ["tenantId"],
|
|
517
|
-
template: "TID#${tenantId}#RT#RoleAssignment"
|
|
598
|
+
field: "GSI1PK",
|
|
599
|
+
composite: ["tenantId", "gsi1Shard"],
|
|
600
|
+
template: "TID#${tenantId}#WID#-#RT#RoleAssignment#SHARD#${gsi1Shard}"
|
|
518
601
|
},
|
|
519
602
|
sk: {
|
|
520
|
-
field: "
|
|
521
|
-
|
|
522
|
-
|
|
603
|
+
field: "GSI1SK",
|
|
604
|
+
casing: "none",
|
|
605
|
+
composite: ["lastUpdated", "id"],
|
|
606
|
+
template: "${lastUpdated}#${id}"
|
|
523
607
|
}
|
|
524
608
|
}
|
|
525
609
|
}
|
|
@@ -555,6 +639,14 @@ var TenantEntity = new import_electrodb5.Entity({
|
|
|
555
639
|
type: "string",
|
|
556
640
|
required: true
|
|
557
641
|
},
|
|
642
|
+
/**
|
|
643
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
644
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
645
|
+
*/
|
|
646
|
+
summary: {
|
|
647
|
+
type: "string",
|
|
648
|
+
required: true
|
|
649
|
+
},
|
|
558
650
|
/** Version id (e.g. ULID). */
|
|
559
651
|
vid: {
|
|
560
652
|
type: "string",
|
|
@@ -564,6 +656,7 @@ var TenantEntity = new import_electrodb5.Entity({
|
|
|
564
656
|
type: "string",
|
|
565
657
|
required: true
|
|
566
658
|
},
|
|
659
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
567
660
|
deleted: {
|
|
568
661
|
type: "boolean",
|
|
569
662
|
required: false
|
|
@@ -591,19 +684,24 @@ var TenantEntity = new import_electrodb5.Entity({
|
|
|
591
684
|
template: "${sk}"
|
|
592
685
|
}
|
|
593
686
|
},
|
|
594
|
-
/**
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
687
|
+
/**
|
|
688
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.
|
|
689
|
+
* Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`
|
|
690
|
+
* sentinels precede `RT#Tenant#SHARD#<n>`. SK is `<ISO-8601 lastUpdated>#<id>` (control-plane
|
|
691
|
+
* unlabeled per DR-004). `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
692
|
+
*/
|
|
693
|
+
gsi1: {
|
|
694
|
+
index: "GSI1",
|
|
598
695
|
pk: {
|
|
599
|
-
field: "
|
|
600
|
-
composite: [],
|
|
601
|
-
template: "RT#Tenant"
|
|
696
|
+
field: "GSI1PK",
|
|
697
|
+
composite: ["gsi1Shard"],
|
|
698
|
+
template: "TID#-#WID#-#RT#Tenant#SHARD#${gsi1Shard}"
|
|
602
699
|
},
|
|
603
700
|
sk: {
|
|
604
|
-
field: "
|
|
605
|
-
|
|
606
|
-
|
|
701
|
+
field: "GSI1SK",
|
|
702
|
+
casing: "none",
|
|
703
|
+
composite: ["lastUpdated", "id"],
|
|
704
|
+
template: "${lastUpdated}#${id}"
|
|
607
705
|
}
|
|
608
706
|
}
|
|
609
707
|
}
|
|
@@ -634,6 +732,22 @@ var UserEntity = new import_electrodb6.Entity({
|
|
|
634
732
|
type: "string",
|
|
635
733
|
required: true
|
|
636
734
|
},
|
|
735
|
+
/**
|
|
736
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
737
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
738
|
+
*/
|
|
739
|
+
summary: {
|
|
740
|
+
type: "string",
|
|
741
|
+
required: true
|
|
742
|
+
},
|
|
743
|
+
/**
|
|
744
|
+
* Immutable Cognito-issued `sub` claim. Drives GSI2 (sub-lookup). Optional until the
|
|
745
|
+
* Post Confirmation Lambda (#770) lands; required thereafter.
|
|
746
|
+
*/
|
|
747
|
+
cognitoSub: {
|
|
748
|
+
type: "string",
|
|
749
|
+
required: false
|
|
750
|
+
},
|
|
637
751
|
/** Version id (e.g. ULID). */
|
|
638
752
|
vid: {
|
|
639
753
|
type: "string",
|
|
@@ -643,6 +757,7 @@ var UserEntity = new import_electrodb6.Entity({
|
|
|
643
757
|
type: "string",
|
|
644
758
|
required: true
|
|
645
759
|
},
|
|
760
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
646
761
|
deleted: {
|
|
647
762
|
type: "boolean",
|
|
648
763
|
required: false
|
|
@@ -670,19 +785,45 @@ var UserEntity = new import_electrodb6.Entity({
|
|
|
670
785
|
template: "${sk}"
|
|
671
786
|
}
|
|
672
787
|
},
|
|
673
|
-
/**
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
788
|
+
/**
|
|
789
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.
|
|
790
|
+
* Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.
|
|
791
|
+
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
792
|
+
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z` characters.
|
|
793
|
+
*/
|
|
794
|
+
gsi1: {
|
|
795
|
+
index: "GSI1",
|
|
677
796
|
pk: {
|
|
678
|
-
field: "
|
|
679
|
-
composite: [],
|
|
680
|
-
template: "RT#User"
|
|
797
|
+
field: "GSI1PK",
|
|
798
|
+
composite: ["gsi1Shard"],
|
|
799
|
+
template: "TID#-#WID#-#RT#User#SHARD#${gsi1Shard}"
|
|
681
800
|
},
|
|
682
801
|
sk: {
|
|
683
|
-
field: "
|
|
684
|
-
|
|
685
|
-
|
|
802
|
+
field: "GSI1SK",
|
|
803
|
+
casing: "none",
|
|
804
|
+
composite: ["lastUpdated", "id"],
|
|
805
|
+
template: "${lastUpdated}#${id}"
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
/**
|
|
809
|
+
* GSI2 — Cognito sub-lookup per ADR-011: resolves the UserEntity from a Cognito `sub` claim.
|
|
810
|
+
* `condition` skips the index when `cognitoSub` is missing so legacy items without a sub are
|
|
811
|
+
* not indexed.
|
|
812
|
+
*/
|
|
813
|
+
gsi2: {
|
|
814
|
+
index: "GSI2",
|
|
815
|
+
condition: (attrs) => typeof attrs.cognitoSub === "string" && attrs.cognitoSub.length > 0,
|
|
816
|
+
pk: {
|
|
817
|
+
field: "GSI2PK",
|
|
818
|
+
casing: "none",
|
|
819
|
+
composite: ["cognitoSub"],
|
|
820
|
+
template: "USER#SUB#${cognitoSub}"
|
|
821
|
+
},
|
|
822
|
+
sk: {
|
|
823
|
+
field: "GSI2SK",
|
|
824
|
+
casing: "none",
|
|
825
|
+
composite: [],
|
|
826
|
+
template: "CURRENT"
|
|
686
827
|
}
|
|
687
828
|
}
|
|
688
829
|
}
|
|
@@ -718,6 +859,14 @@ var WorkspaceEntity = new import_electrodb7.Entity({
|
|
|
718
859
|
type: "string",
|
|
719
860
|
required: true
|
|
720
861
|
},
|
|
862
|
+
/**
|
|
863
|
+
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
864
|
+
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
865
|
+
*/
|
|
866
|
+
summary: {
|
|
867
|
+
type: "string",
|
|
868
|
+
required: true
|
|
869
|
+
},
|
|
721
870
|
/** Version id (e.g. ULID). */
|
|
722
871
|
vid: {
|
|
723
872
|
type: "string",
|
|
@@ -727,6 +876,7 @@ var WorkspaceEntity = new import_electrodb7.Entity({
|
|
|
727
876
|
type: "string",
|
|
728
877
|
required: true
|
|
729
878
|
},
|
|
879
|
+
gsi1Shard: gsi1ShardAttribute,
|
|
730
880
|
deleted: {
|
|
731
881
|
type: "boolean",
|
|
732
882
|
required: false
|
|
@@ -754,19 +904,24 @@ var WorkspaceEntity = new import_electrodb7.Entity({
|
|
|
754
904
|
template: "${sk}"
|
|
755
905
|
}
|
|
756
906
|
},
|
|
757
|
-
/**
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
907
|
+
/**
|
|
908
|
+
* GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the
|
|
909
|
+
* four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.
|
|
910
|
+
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
911
|
+
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
912
|
+
*/
|
|
913
|
+
gsi1: {
|
|
914
|
+
index: "GSI1",
|
|
761
915
|
pk: {
|
|
762
|
-
field: "
|
|
763
|
-
composite: ["tenantId"],
|
|
764
|
-
template: "TID#${tenantId}#RT#Workspace"
|
|
916
|
+
field: "GSI1PK",
|
|
917
|
+
composite: ["tenantId", "gsi1Shard"],
|
|
918
|
+
template: "TID#${tenantId}#WID#-#RT#Workspace#SHARD#${gsi1Shard}"
|
|
765
919
|
},
|
|
766
920
|
sk: {
|
|
767
|
-
field: "
|
|
768
|
-
|
|
769
|
-
|
|
921
|
+
field: "GSI1SK",
|
|
922
|
+
casing: "none",
|
|
923
|
+
composite: ["lastUpdated", "id"],
|
|
924
|
+
template: "${lastUpdated}#${id}"
|
|
770
925
|
}
|
|
771
926
|
}
|
|
772
927
|
}
|
|
@@ -820,6 +975,7 @@ async function createConfigurationOperation(params) {
|
|
|
820
975
|
const roleId = body.roleId ?? context.roleId ?? "-";
|
|
821
976
|
const lastUpdated = body.lastUpdated ?? date;
|
|
822
977
|
const vid = body.vid ?? (date.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36));
|
|
978
|
+
const summary = JSON.stringify({ resourceType: "Configuration", id, key });
|
|
823
979
|
const service = getDynamoControlService(tableName);
|
|
824
980
|
await service.entities.configuration.put({
|
|
825
981
|
tenantId,
|
|
@@ -829,6 +985,7 @@ async function createConfigurationOperation(params) {
|
|
|
829
985
|
key,
|
|
830
986
|
id,
|
|
831
987
|
resource: compressResource(resourceStr),
|
|
988
|
+
summary,
|
|
832
989
|
vid,
|
|
833
990
|
lastUpdated,
|
|
834
991
|
sk: SK
|
|
@@ -1235,22 +1392,25 @@ async function listConfigurationsOperation(params) {
|
|
|
1235
1392
|
const { context, tableName } = params;
|
|
1236
1393
|
const { tenantId, workspaceId } = context;
|
|
1237
1394
|
const service = getDynamoControlService(tableName);
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1395
|
+
const shardResults = await Promise.all(
|
|
1396
|
+
Array.from(
|
|
1397
|
+
{ length: SHARD_COUNT },
|
|
1398
|
+
(_, shard) => service.entities.configuration.query.gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) }).go()
|
|
1399
|
+
)
|
|
1400
|
+
);
|
|
1401
|
+
const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
|
|
1402
|
+
const resource = JSON.parse(decompressResource(item.resource));
|
|
1403
|
+
return {
|
|
1404
|
+
id: item.id,
|
|
1405
|
+
key: item.key,
|
|
1406
|
+
resource: {
|
|
1407
|
+
resourceType: "Configuration",
|
|
1243
1408
|
id: item.id,
|
|
1244
1409
|
key: item.key,
|
|
1245
|
-
resource
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
resource
|
|
1250
|
-
}
|
|
1251
|
-
};
|
|
1252
|
-
}
|
|
1253
|
-
);
|
|
1410
|
+
resource
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
});
|
|
1254
1414
|
return { entries };
|
|
1255
1415
|
}
|
|
1256
1416
|
|
|
@@ -1420,8 +1580,14 @@ async function updateConfigurationOperation(params) {
|
|
|
1420
1580
|
const resourceStr = typeof resourcePayload === "string" ? resourcePayload : JSON.stringify(resourcePayload ?? {});
|
|
1421
1581
|
const lastUpdated = body.lastUpdated ?? date;
|
|
1422
1582
|
const nextVid = existing.data.vid != null ? String(Number(existing.data.vid) + 1) : date.replace(/[-:T.Z]/g, "").slice(0, 12) || "2";
|
|
1583
|
+
const summary = JSON.stringify({
|
|
1584
|
+
resourceType: "Configuration",
|
|
1585
|
+
id: existing.data.id,
|
|
1586
|
+
key: existing.data.key
|
|
1587
|
+
});
|
|
1423
1588
|
await service.entities.configuration.patch({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK4 }).set({
|
|
1424
1589
|
resource: compressResource(resourceStr),
|
|
1590
|
+
summary,
|
|
1425
1591
|
lastUpdated,
|
|
1426
1592
|
vid: nextVid
|
|
1427
1593
|
}).go();
|
|
@@ -1499,6 +1665,7 @@ router.delete("/:id", deleteConfigurationRoute);
|
|
|
1499
1665
|
var import_express2 = __toESM(require("express"));
|
|
1500
1666
|
|
|
1501
1667
|
// src/data/operations/control/membership/membership-create-operation.ts
|
|
1668
|
+
var import_types = require("@openhi/types");
|
|
1502
1669
|
async function createMembershipOperation(params) {
|
|
1503
1670
|
const { context, body, tableName } = params;
|
|
1504
1671
|
const service = getDynamoControlService(tableName);
|
|
@@ -1506,20 +1673,19 @@ async function createMembershipOperation(params) {
|
|
|
1506
1673
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
1507
1674
|
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1508
1675
|
const vid = `1`;
|
|
1676
|
+
const resource = { resourceType: "Membership", id, ...parsedResource };
|
|
1677
|
+
const summary = JSON.stringify((0, import_types.extractSummary)(resource));
|
|
1509
1678
|
await service.entities.membership.put({
|
|
1510
1679
|
tenantId: context.tenantId,
|
|
1511
1680
|
id,
|
|
1512
|
-
resource: JSON.stringify(
|
|
1513
|
-
|
|
1514
|
-
id,
|
|
1515
|
-
...parsedResource
|
|
1516
|
-
}),
|
|
1681
|
+
resource: JSON.stringify(resource),
|
|
1682
|
+
summary,
|
|
1517
1683
|
vid,
|
|
1518
1684
|
lastUpdated
|
|
1519
1685
|
}).go();
|
|
1520
1686
|
return {
|
|
1521
1687
|
id,
|
|
1522
|
-
resource
|
|
1688
|
+
resource,
|
|
1523
1689
|
meta: { lastUpdated, versionId: vid }
|
|
1524
1690
|
};
|
|
1525
1691
|
}
|
|
@@ -1627,12 +1793,21 @@ async function getMembershipByIdRoute(req, res) {
|
|
|
1627
1793
|
async function listMembershipsOperation(params) {
|
|
1628
1794
|
const { context, tableName } = params;
|
|
1629
1795
|
const service = getDynamoControlService(tableName);
|
|
1630
|
-
const
|
|
1631
|
-
|
|
1796
|
+
const shardResults = await Promise.all(
|
|
1797
|
+
Array.from(
|
|
1798
|
+
{ length: SHARD_COUNT },
|
|
1799
|
+
(_, shard) => service.entities.membership.query.gsi1({ tenantId: context.tenantId, gsi1Shard: String(shard) }).go()
|
|
1800
|
+
)
|
|
1801
|
+
);
|
|
1802
|
+
const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
|
|
1632
1803
|
const parsedResource = JSON.parse(item.resource);
|
|
1633
1804
|
return {
|
|
1634
1805
|
id: item.id,
|
|
1635
|
-
resource: {
|
|
1806
|
+
resource: {
|
|
1807
|
+
resourceType: "Membership",
|
|
1808
|
+
id: item.id,
|
|
1809
|
+
...parsedResource
|
|
1810
|
+
}
|
|
1636
1811
|
};
|
|
1637
1812
|
});
|
|
1638
1813
|
return { entries };
|
|
@@ -1661,6 +1836,7 @@ async function listMembershipsRoute(req, res) {
|
|
|
1661
1836
|
}
|
|
1662
1837
|
|
|
1663
1838
|
// src/data/operations/control/membership/membership-update-operation.ts
|
|
1839
|
+
var import_types2 = require("@openhi/types");
|
|
1664
1840
|
async function updateMembershipOperation(params) {
|
|
1665
1841
|
const { context, id, body, tableName } = params;
|
|
1666
1842
|
const service = getDynamoControlService(tableName);
|
|
@@ -1671,20 +1847,19 @@ async function updateMembershipOperation(params) {
|
|
|
1671
1847
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
1672
1848
|
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1673
1849
|
const vid = `${Date.now()}`;
|
|
1850
|
+
const resource = { resourceType: "Membership", id, ...parsedResource };
|
|
1851
|
+
const summary = JSON.stringify((0, import_types2.extractSummary)(resource));
|
|
1674
1852
|
await service.entities.membership.put({
|
|
1675
1853
|
tenantId: context.tenantId,
|
|
1676
1854
|
id,
|
|
1677
|
-
resource: JSON.stringify(
|
|
1678
|
-
|
|
1679
|
-
id,
|
|
1680
|
-
...parsedResource
|
|
1681
|
-
}),
|
|
1855
|
+
resource: JSON.stringify(resource),
|
|
1856
|
+
summary,
|
|
1682
1857
|
vid,
|
|
1683
1858
|
lastUpdated
|
|
1684
1859
|
}).go();
|
|
1685
1860
|
return {
|
|
1686
1861
|
id,
|
|
1687
|
-
resource
|
|
1862
|
+
resource,
|
|
1688
1863
|
meta: { lastUpdated, versionId: vid }
|
|
1689
1864
|
};
|
|
1690
1865
|
}
|
|
@@ -1741,6 +1916,7 @@ router2.delete("/:id", deleteMembershipRoute);
|
|
|
1741
1916
|
var import_express3 = __toESM(require("express"));
|
|
1742
1917
|
|
|
1743
1918
|
// src/data/operations/control/role/role-create-operation.ts
|
|
1919
|
+
var import_types3 = require("@openhi/types");
|
|
1744
1920
|
async function createRoleOperation(params) {
|
|
1745
1921
|
const { context, body, tableName } = params;
|
|
1746
1922
|
const service = getDynamoControlService(tableName);
|
|
@@ -1748,15 +1924,18 @@ async function createRoleOperation(params) {
|
|
|
1748
1924
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
1749
1925
|
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1750
1926
|
const vid = `1`;
|
|
1927
|
+
const resource = { resourceType: "Role", id, ...parsedResource };
|
|
1928
|
+
const summary = JSON.stringify((0, import_types3.extractSummary)(resource));
|
|
1751
1929
|
await service.entities.role.put({
|
|
1752
1930
|
id,
|
|
1753
|
-
resource: JSON.stringify(
|
|
1931
|
+
resource: JSON.stringify(resource),
|
|
1932
|
+
summary,
|
|
1754
1933
|
vid,
|
|
1755
1934
|
lastUpdated
|
|
1756
1935
|
}).go();
|
|
1757
1936
|
return {
|
|
1758
1937
|
id,
|
|
1759
|
-
resource
|
|
1938
|
+
resource,
|
|
1760
1939
|
meta: { lastUpdated, versionId: vid }
|
|
1761
1940
|
};
|
|
1762
1941
|
}
|
|
@@ -1864,8 +2043,13 @@ async function getRoleByIdRoute(req, res) {
|
|
|
1864
2043
|
async function listRolesOperation(params) {
|
|
1865
2044
|
const { tableName } = params;
|
|
1866
2045
|
const service = getDynamoControlService(tableName);
|
|
1867
|
-
const
|
|
1868
|
-
|
|
2046
|
+
const shardResults = await Promise.all(
|
|
2047
|
+
Array.from(
|
|
2048
|
+
{ length: SHARD_COUNT },
|
|
2049
|
+
(_, shard) => service.entities.role.query.gsi1({ gsi1Shard: String(shard) }).go()
|
|
2050
|
+
)
|
|
2051
|
+
);
|
|
2052
|
+
const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
|
|
1869
2053
|
const parsedResource = JSON.parse(item.resource);
|
|
1870
2054
|
return {
|
|
1871
2055
|
id: item.id,
|
|
@@ -1898,6 +2082,7 @@ async function listRolesRoute(req, res) {
|
|
|
1898
2082
|
}
|
|
1899
2083
|
|
|
1900
2084
|
// src/data/operations/control/role/role-update-operation.ts
|
|
2085
|
+
var import_types4 = require("@openhi/types");
|
|
1901
2086
|
async function updateRoleOperation(params) {
|
|
1902
2087
|
const { context, id, body, tableName } = params;
|
|
1903
2088
|
const service = getDynamoControlService(tableName);
|
|
@@ -1908,15 +2093,18 @@ async function updateRoleOperation(params) {
|
|
|
1908
2093
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
1909
2094
|
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1910
2095
|
const vid = `${Date.now()}`;
|
|
2096
|
+
const resource = { resourceType: "Role", id, ...parsedResource };
|
|
2097
|
+
const summary = JSON.stringify((0, import_types4.extractSummary)(resource));
|
|
1911
2098
|
await service.entities.role.put({
|
|
1912
2099
|
id,
|
|
1913
|
-
resource: JSON.stringify(
|
|
2100
|
+
resource: JSON.stringify(resource),
|
|
2101
|
+
summary,
|
|
1914
2102
|
vid,
|
|
1915
2103
|
lastUpdated
|
|
1916
2104
|
}).go();
|
|
1917
2105
|
return {
|
|
1918
2106
|
id,
|
|
1919
|
-
resource
|
|
2107
|
+
resource,
|
|
1920
2108
|
meta: { lastUpdated, versionId: vid }
|
|
1921
2109
|
};
|
|
1922
2110
|
}
|
|
@@ -1973,6 +2161,7 @@ router3.delete("/:id", deleteRoleRoute);
|
|
|
1973
2161
|
var import_express4 = __toESM(require("express"));
|
|
1974
2162
|
|
|
1975
2163
|
// src/data/operations/control/roleassignment/roleassignment-create-operation.ts
|
|
2164
|
+
var import_types5 = require("@openhi/types");
|
|
1976
2165
|
async function createRoleAssignmentOperation(params) {
|
|
1977
2166
|
const { context, body, tableName } = params;
|
|
1978
2167
|
const service = getDynamoControlService(tableName);
|
|
@@ -1980,20 +2169,19 @@ async function createRoleAssignmentOperation(params) {
|
|
|
1980
2169
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
1981
2170
|
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1982
2171
|
const vid = `1`;
|
|
2172
|
+
const resource = { resourceType: "RoleAssignment", id, ...parsedResource };
|
|
2173
|
+
const summary = JSON.stringify((0, import_types5.extractSummary)(resource));
|
|
1983
2174
|
await service.entities.roleAssignment.put({
|
|
1984
2175
|
tenantId: context.tenantId,
|
|
1985
2176
|
id,
|
|
1986
|
-
resource: JSON.stringify(
|
|
1987
|
-
|
|
1988
|
-
id,
|
|
1989
|
-
...parsedResource
|
|
1990
|
-
}),
|
|
2177
|
+
resource: JSON.stringify(resource),
|
|
2178
|
+
summary,
|
|
1991
2179
|
vid,
|
|
1992
2180
|
lastUpdated
|
|
1993
2181
|
}).go();
|
|
1994
2182
|
return {
|
|
1995
2183
|
id,
|
|
1996
|
-
resource
|
|
2184
|
+
resource,
|
|
1997
2185
|
meta: { lastUpdated, versionId: vid }
|
|
1998
2186
|
};
|
|
1999
2187
|
}
|
|
@@ -2101,8 +2289,13 @@ async function getRoleAssignmentByIdRoute(req, res) {
|
|
|
2101
2289
|
async function listRoleAssignmentsOperation(params) {
|
|
2102
2290
|
const { context, tableName } = params;
|
|
2103
2291
|
const service = getDynamoControlService(tableName);
|
|
2104
|
-
const
|
|
2105
|
-
|
|
2292
|
+
const shardResults = await Promise.all(
|
|
2293
|
+
Array.from(
|
|
2294
|
+
{ length: SHARD_COUNT },
|
|
2295
|
+
(_, shard) => service.entities.roleAssignment.query.gsi1({ tenantId: context.tenantId, gsi1Shard: String(shard) }).go()
|
|
2296
|
+
)
|
|
2297
|
+
);
|
|
2298
|
+
const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
|
|
2106
2299
|
const parsedResource = JSON.parse(item.resource);
|
|
2107
2300
|
return {
|
|
2108
2301
|
id: item.id,
|
|
@@ -2139,6 +2332,7 @@ async function listRoleAssignmentsRoute(req, res) {
|
|
|
2139
2332
|
}
|
|
2140
2333
|
|
|
2141
2334
|
// src/data/operations/control/roleassignment/roleassignment-update-operation.ts
|
|
2335
|
+
var import_types6 = require("@openhi/types");
|
|
2142
2336
|
async function updateRoleAssignmentOperation(params) {
|
|
2143
2337
|
const { context, id, body, tableName } = params;
|
|
2144
2338
|
const service = getDynamoControlService(tableName);
|
|
@@ -2149,20 +2343,19 @@ async function updateRoleAssignmentOperation(params) {
|
|
|
2149
2343
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
2150
2344
|
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2151
2345
|
const vid = `${Date.now()}`;
|
|
2346
|
+
const resource = { resourceType: "RoleAssignment", id, ...parsedResource };
|
|
2347
|
+
const summary = JSON.stringify((0, import_types6.extractSummary)(resource));
|
|
2152
2348
|
await service.entities.roleAssignment.put({
|
|
2153
2349
|
tenantId: context.tenantId,
|
|
2154
2350
|
id,
|
|
2155
|
-
resource: JSON.stringify(
|
|
2156
|
-
|
|
2157
|
-
id,
|
|
2158
|
-
...parsedResource
|
|
2159
|
-
}),
|
|
2351
|
+
resource: JSON.stringify(resource),
|
|
2352
|
+
summary,
|
|
2160
2353
|
vid,
|
|
2161
2354
|
lastUpdated
|
|
2162
2355
|
}).go();
|
|
2163
2356
|
return {
|
|
2164
2357
|
id,
|
|
2165
|
-
resource
|
|
2358
|
+
resource,
|
|
2166
2359
|
meta: { lastUpdated, versionId: vid }
|
|
2167
2360
|
};
|
|
2168
2361
|
}
|
|
@@ -2219,6 +2412,7 @@ router4.delete("/:id", deleteRoleAssignmentRoute);
|
|
|
2219
2412
|
var import_express5 = __toESM(require("express"));
|
|
2220
2413
|
|
|
2221
2414
|
// src/data/operations/control/tenant/tenant-create-operation.ts
|
|
2415
|
+
var import_types7 = require("@openhi/types");
|
|
2222
2416
|
async function createTenantOperation(params) {
|
|
2223
2417
|
const { context, body, tableName } = params;
|
|
2224
2418
|
const service = getDynamoControlService(tableName);
|
|
@@ -2227,10 +2421,12 @@ async function createTenantOperation(params) {
|
|
|
2227
2421
|
const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
|
|
2228
2422
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
2229
2423
|
const resource = { resourceType: "Tenant", id, ...parsedResource };
|
|
2424
|
+
const summary = JSON.stringify((0, import_types7.extractSummary)(resource));
|
|
2230
2425
|
await service.entities.tenant.put({
|
|
2231
2426
|
tenantId: id,
|
|
2232
2427
|
id,
|
|
2233
2428
|
resource: JSON.stringify(resource),
|
|
2429
|
+
summary,
|
|
2234
2430
|
vid,
|
|
2235
2431
|
lastUpdated
|
|
2236
2432
|
}).go();
|
|
@@ -2339,8 +2535,13 @@ async function getTenantByIdRoute(req, res) {
|
|
|
2339
2535
|
async function listTenantsOperation(params) {
|
|
2340
2536
|
const { tableName } = params;
|
|
2341
2537
|
const service = getDynamoControlService(tableName);
|
|
2342
|
-
const
|
|
2343
|
-
|
|
2538
|
+
const shardResults = await Promise.all(
|
|
2539
|
+
Array.from(
|
|
2540
|
+
{ length: SHARD_COUNT },
|
|
2541
|
+
(_, shard) => service.entities.tenant.query.gsi1({ gsi1Shard: String(shard) }).go()
|
|
2542
|
+
)
|
|
2543
|
+
);
|
|
2544
|
+
const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
|
|
2344
2545
|
const parsed = JSON.parse(item.resource);
|
|
2345
2546
|
return {
|
|
2346
2547
|
id: item.id,
|
|
@@ -2373,6 +2574,7 @@ async function listTenantsRoute(req, res) {
|
|
|
2373
2574
|
}
|
|
2374
2575
|
|
|
2375
2576
|
// src/data/operations/control/tenant/tenant-update-operation.ts
|
|
2577
|
+
var import_types8 = require("@openhi/types");
|
|
2376
2578
|
async function updateTenantOperation(params) {
|
|
2377
2579
|
const { context, id, body, tableName } = params;
|
|
2378
2580
|
const service = getDynamoControlService(tableName);
|
|
@@ -2390,7 +2592,8 @@ async function updateTenantOperation(params) {
|
|
|
2390
2592
|
resourceType: "Tenant",
|
|
2391
2593
|
id
|
|
2392
2594
|
};
|
|
2393
|
-
|
|
2595
|
+
const summary = JSON.stringify((0, import_types8.extractSummary)(updated));
|
|
2596
|
+
await service.entities.tenant.patch({ tenantId: id, sk: "CURRENT" }).set({ resource: JSON.stringify(updated), summary, vid, lastUpdated }).go();
|
|
2394
2597
|
return { id, resource: updated, meta: { lastUpdated, versionId: vid } };
|
|
2395
2598
|
}
|
|
2396
2599
|
|
|
@@ -2446,6 +2649,7 @@ router5.delete("/:id", deleteTenantRoute);
|
|
|
2446
2649
|
var import_express6 = __toESM(require("express"));
|
|
2447
2650
|
|
|
2448
2651
|
// src/data/operations/control/user/user-create-operation.ts
|
|
2652
|
+
var import_types9 = require("@openhi/types");
|
|
2449
2653
|
async function createUserOperation(params) {
|
|
2450
2654
|
const { context, body, tableName } = params;
|
|
2451
2655
|
const service = getDynamoControlService(tableName);
|
|
@@ -2453,15 +2657,18 @@ async function createUserOperation(params) {
|
|
|
2453
2657
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
2454
2658
|
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2455
2659
|
const vid = `1`;
|
|
2660
|
+
const resource = { resourceType: "User", id, ...parsedResource };
|
|
2661
|
+
const summary = JSON.stringify((0, import_types9.extractSummary)(resource));
|
|
2456
2662
|
await service.entities.user.put({
|
|
2457
2663
|
id,
|
|
2458
|
-
resource: JSON.stringify(
|
|
2664
|
+
resource: JSON.stringify(resource),
|
|
2665
|
+
summary,
|
|
2459
2666
|
vid,
|
|
2460
2667
|
lastUpdated
|
|
2461
2668
|
}).go();
|
|
2462
2669
|
return {
|
|
2463
2670
|
id,
|
|
2464
|
-
resource
|
|
2671
|
+
resource,
|
|
2465
2672
|
meta: { lastUpdated, versionId: vid }
|
|
2466
2673
|
};
|
|
2467
2674
|
}
|
|
@@ -2569,8 +2776,13 @@ async function getUserByIdRoute(req, res) {
|
|
|
2569
2776
|
async function listUsersOperation(params) {
|
|
2570
2777
|
const { tableName } = params;
|
|
2571
2778
|
const service = getDynamoControlService(tableName);
|
|
2572
|
-
const
|
|
2573
|
-
|
|
2779
|
+
const shardResults = await Promise.all(
|
|
2780
|
+
Array.from(
|
|
2781
|
+
{ length: SHARD_COUNT },
|
|
2782
|
+
(_, shard) => service.entities.user.query.gsi1({ gsi1Shard: String(shard) }).go()
|
|
2783
|
+
)
|
|
2784
|
+
);
|
|
2785
|
+
const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
|
|
2574
2786
|
const parsedResource = JSON.parse(item.resource);
|
|
2575
2787
|
return {
|
|
2576
2788
|
id: item.id,
|
|
@@ -2603,6 +2815,7 @@ async function listUsersRoute(req, res) {
|
|
|
2603
2815
|
}
|
|
2604
2816
|
|
|
2605
2817
|
// src/data/operations/control/user/user-update-operation.ts
|
|
2818
|
+
var import_types10 = require("@openhi/types");
|
|
2606
2819
|
async function updateUserOperation(params) {
|
|
2607
2820
|
const { context, id, body, tableName } = params;
|
|
2608
2821
|
const service = getDynamoControlService(tableName);
|
|
@@ -2613,15 +2826,18 @@ async function updateUserOperation(params) {
|
|
|
2613
2826
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
2614
2827
|
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2615
2828
|
const vid = `${Date.now()}`;
|
|
2829
|
+
const resource = { resourceType: "User", id, ...parsedResource };
|
|
2830
|
+
const summary = JSON.stringify((0, import_types10.extractSummary)(resource));
|
|
2616
2831
|
await service.entities.user.put({
|
|
2617
2832
|
id,
|
|
2618
|
-
resource: JSON.stringify(
|
|
2833
|
+
resource: JSON.stringify(resource),
|
|
2834
|
+
summary,
|
|
2619
2835
|
vid,
|
|
2620
2836
|
lastUpdated
|
|
2621
2837
|
}).go();
|
|
2622
2838
|
return {
|
|
2623
2839
|
id,
|
|
2624
|
-
resource
|
|
2840
|
+
resource,
|
|
2625
2841
|
meta: { lastUpdated, versionId: vid }
|
|
2626
2842
|
};
|
|
2627
2843
|
}
|
|
@@ -2678,6 +2894,7 @@ router6.delete("/:id", deleteUserRoute);
|
|
|
2678
2894
|
var import_express7 = __toESM(require("express"));
|
|
2679
2895
|
|
|
2680
2896
|
// src/data/operations/control/workspace/workspace-create-operation.ts
|
|
2897
|
+
var import_types11 = require("@openhi/types");
|
|
2681
2898
|
async function createWorkspaceOperation(params) {
|
|
2682
2899
|
const { context, body, tableName } = params;
|
|
2683
2900
|
const { tenantId } = context;
|
|
@@ -2687,7 +2904,15 @@ async function createWorkspaceOperation(params) {
|
|
|
2687
2904
|
const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
|
|
2688
2905
|
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
2689
2906
|
const resource = { resourceType: "Workspace", id, ...parsedResource };
|
|
2690
|
-
|
|
2907
|
+
const summary = JSON.stringify((0, import_types11.extractSummary)(resource));
|
|
2908
|
+
await service.entities.workspace.put({
|
|
2909
|
+
tenantId,
|
|
2910
|
+
id,
|
|
2911
|
+
resource: JSON.stringify(resource),
|
|
2912
|
+
summary,
|
|
2913
|
+
vid,
|
|
2914
|
+
lastUpdated
|
|
2915
|
+
}).go();
|
|
2691
2916
|
return { id, resource, meta: { lastUpdated, versionId: vid } };
|
|
2692
2917
|
}
|
|
2693
2918
|
|
|
@@ -2796,8 +3021,13 @@ async function listWorkspacesOperation(params) {
|
|
|
2796
3021
|
const { context, tableName } = params;
|
|
2797
3022
|
const { tenantId } = context;
|
|
2798
3023
|
const service = getDynamoControlService(tableName);
|
|
2799
|
-
const
|
|
2800
|
-
|
|
3024
|
+
const shardResults = await Promise.all(
|
|
3025
|
+
Array.from(
|
|
3026
|
+
{ length: SHARD_COUNT },
|
|
3027
|
+
(_, shard) => service.entities.workspace.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
|
|
3028
|
+
)
|
|
3029
|
+
);
|
|
3030
|
+
const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
|
|
2801
3031
|
const parsed = JSON.parse(item.resource);
|
|
2802
3032
|
return {
|
|
2803
3033
|
id: item.id,
|
|
@@ -2830,6 +3060,7 @@ async function listWorkspacesRoute(req, res) {
|
|
|
2830
3060
|
}
|
|
2831
3061
|
|
|
2832
3062
|
// src/data/operations/control/workspace/workspace-update-operation.ts
|
|
3063
|
+
var import_types12 = require("@openhi/types");
|
|
2833
3064
|
async function updateWorkspaceOperation(params) {
|
|
2834
3065
|
const { context, id, body, tableName } = params;
|
|
2835
3066
|
const { tenantId } = context;
|
|
@@ -2848,7 +3079,8 @@ async function updateWorkspaceOperation(params) {
|
|
|
2848
3079
|
resourceType: "Workspace",
|
|
2849
3080
|
id
|
|
2850
3081
|
};
|
|
2851
|
-
|
|
3082
|
+
const summary = JSON.stringify((0, import_types12.extractSummary)(updated));
|
|
3083
|
+
await service.entities.workspace.patch({ tenantId, id, sk: "CURRENT" }).set({ resource: JSON.stringify(updated), summary, vid, lastUpdated }).go();
|
|
2852
3084
|
return { id, resource: updated, meta: { lastUpdated, versionId: vid } };
|
|
2853
3085
|
}
|
|
2854
3086
|
|
|
@@ -2960,6 +3192,18 @@ var dataEntityAttributes = {
|
|
|
2960
3192
|
type: "string",
|
|
2961
3193
|
required: true
|
|
2962
3194
|
},
|
|
3195
|
+
/**
|
|
3196
|
+
* Summary projection of the FHIR resource as a JSON string (uncompressed). Populated on every
|
|
3197
|
+
* write via `extractSummary(resource)` so GSI projections can surface list/lookup data without
|
|
3198
|
+
* reading the compressed `resource` blob. Kept uncompressed because the summary is small and
|
|
3199
|
+
* must be fast to retrieve without encode/decode overhead.
|
|
3200
|
+
*
|
|
3201
|
+
* @see sites/www-docs/content/architecture/adr/2026-04-17-02-fhir-summary-projection-for-gsi-access-patterns.md
|
|
3202
|
+
*/
|
|
3203
|
+
summary: {
|
|
3204
|
+
type: "string",
|
|
3205
|
+
required: true
|
|
3206
|
+
},
|
|
2963
3207
|
/** Version id (e.g. ULID). Tracks current version; S3 history key. */
|
|
2964
3208
|
vid: {
|
|
2965
3209
|
type: "string",
|
|
@@ -2969,6 +3213,41 @@ var dataEntityAttributes = {
|
|
|
2969
3213
|
type: "string",
|
|
2970
3214
|
required: true
|
|
2971
3215
|
},
|
|
3216
|
+
/**
|
|
3217
|
+
* Shard index segment for the GSI1 partition key. Computed deterministically from `id`
|
|
3218
|
+
* via `computeShard` so updates always land on the same shard. Stored as a string because
|
|
3219
|
+
* it appears as a literal segment in the GSI1 PK template; the underlying value is 0..3.
|
|
3220
|
+
* Not `required` because the value is derived via `watch`/`set`; ElectroDB's required-field
|
|
3221
|
+
* check runs before watch propagation, so callers must not fail validation on a derived field.
|
|
3222
|
+
*
|
|
3223
|
+
* @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md — GSI1 (sharded)
|
|
3224
|
+
*/
|
|
3225
|
+
gsi1Shard: {
|
|
3226
|
+
type: "string",
|
|
3227
|
+
watch: ["id"],
|
|
3228
|
+
set: (_val, item) => {
|
|
3229
|
+
if (typeof item?.id !== "string" || item.id.length === 0) {
|
|
3230
|
+
return void 0;
|
|
3231
|
+
}
|
|
3232
|
+
return String(computeShard(item.id));
|
|
3233
|
+
}
|
|
3234
|
+
},
|
|
3235
|
+
/**
|
|
3236
|
+
* GSI1 sort key. Written as the index's SK verbatim so list endpoints can
|
|
3237
|
+
* use `BEGINS_WITH` for prefix queries (e.g. `?name=Sm` against Patient).
|
|
3238
|
+
* Computed at write time via `extractSortKey(resource)` per DR-004:
|
|
3239
|
+
* - Labeled types (LABEL_PATHS): `<normalizedLabel>#<id>`
|
|
3240
|
+
* - Unlabeled types: `<ISO-8601 lastUpdated>#<id>`
|
|
3241
|
+
* The factory deliberately does not derive this from `lastUpdated`/`id`
|
|
3242
|
+
* — that would lock every type into the unlabeled fallback and defeat
|
|
3243
|
+
* label-based BEGINS_WITH on labeled types.
|
|
3244
|
+
*
|
|
3245
|
+
* @see openhi-planning DR-004
|
|
3246
|
+
*/
|
|
3247
|
+
gsi1sk: {
|
|
3248
|
+
type: "string",
|
|
3249
|
+
required: true
|
|
3250
|
+
},
|
|
2972
3251
|
deleted: {
|
|
2973
3252
|
type: "boolean",
|
|
2974
3253
|
required: false
|
|
@@ -3003,18 +3282,26 @@ function createDataEntity(entity, resourceTypeLabel) {
|
|
|
3003
3282
|
composite: ["sk"]
|
|
3004
3283
|
}
|
|
3005
3284
|
},
|
|
3006
|
-
/**
|
|
3007
|
-
|
|
3008
|
-
|
|
3285
|
+
/**
|
|
3286
|
+
* GSI1 — Unified Sharded List: list all resources of this type in a workspace; reads fan
|
|
3287
|
+
* out across the four shards and merge by SK. SK is the writer-supplied `gsi1sk` verbatim
|
|
3288
|
+
* (per DR-004) so labeled types serve `BEGINS_WITH` prefix queries on the natural label.
|
|
3289
|
+
* `casing: "none"` is required on the SK because the writer (`extractSortKey`) already
|
|
3290
|
+
* applies DR-004 normalization — ElectroDB's default lowercasing would mangle the
|
|
3291
|
+
* ISO-8601 unlabeled fallback (`T`/`Z` → `t`/`z`).
|
|
3292
|
+
*/
|
|
3293
|
+
gsi1: {
|
|
3294
|
+
index: "GSI1",
|
|
3009
3295
|
pk: {
|
|
3010
|
-
field: "
|
|
3011
|
-
composite: ["tenantId", "workspaceId"],
|
|
3012
|
-
template: `TID#\${tenantId}#WID#\${workspaceId}#RT#${resourceTypeLabel}`
|
|
3296
|
+
field: "GSI1PK",
|
|
3297
|
+
composite: ["tenantId", "workspaceId", "gsi1Shard"],
|
|
3298
|
+
template: `TID#\${tenantId}#WID#\${workspaceId}#RT#${resourceTypeLabel}#SHARD#\${gsi1Shard}`
|
|
3013
3299
|
},
|
|
3014
3300
|
sk: {
|
|
3015
|
-
field: "
|
|
3016
|
-
|
|
3017
|
-
|
|
3301
|
+
field: "GSI1SK",
|
|
3302
|
+
casing: "none",
|
|
3303
|
+
composite: ["gsi1sk"],
|
|
3304
|
+
template: `\${gsi1sk}`
|
|
3018
3305
|
}
|
|
3019
3306
|
}
|
|
3020
3307
|
}
|
|
@@ -3908,6 +4195,7 @@ function getDynamoDataService(tableName) {
|
|
|
3908
4195
|
}
|
|
3909
4196
|
|
|
3910
4197
|
// src/data/operations/data-operations-common.ts
|
|
4198
|
+
var import_types13 = require("@openhi/types");
|
|
3911
4199
|
var DATA_ENTITY_SK = "CURRENT";
|
|
3912
4200
|
async function getDataEntityById(entity, tenantId, workspaceId, id, resourceLabel) {
|
|
3913
4201
|
const result = await entity.get({
|
|
@@ -3936,28 +4224,40 @@ async function deleteDataEntityById(entity, tenantId, workspaceId, id) {
|
|
|
3936
4224
|
}).go();
|
|
3937
4225
|
}
|
|
3938
4226
|
async function listDataEntitiesByWorkspace(entity, tenantId, workspaceId) {
|
|
3939
|
-
const
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
4227
|
+
const shardResults = await Promise.all(
|
|
4228
|
+
Array.from(
|
|
4229
|
+
{ length: SHARD_COUNT },
|
|
4230
|
+
(_, shard) => entity.query.gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) }).go()
|
|
4231
|
+
)
|
|
4232
|
+
);
|
|
4233
|
+
const entries = [];
|
|
4234
|
+
for (const shardResult of shardResults) {
|
|
4235
|
+
for (const item of shardResult.data ?? []) {
|
|
4236
|
+
const parsed = JSON.parse(decompressResource(item.resource));
|
|
4237
|
+
entries.push({
|
|
4238
|
+
id: item.id,
|
|
4239
|
+
resource: { ...parsed, id: item.id }
|
|
4240
|
+
});
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
3948
4243
|
return { entries };
|
|
3949
4244
|
}
|
|
3950
4245
|
async function createDataEntityRecord(entity, tenantId, workspaceId, id, resourceWithAudit, fallbackDate) {
|
|
3951
4246
|
const lastUpdated = resourceWithAudit.meta?.lastUpdated ?? fallbackDate ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3952
4247
|
const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
|
|
4248
|
+
const resourceLike = resourceWithAudit;
|
|
4249
|
+
const summary = JSON.stringify((0, import_types13.extractSummary)(resourceLike));
|
|
4250
|
+
const gsi1sk = (0, import_types13.extractSortKey)(resourceLike);
|
|
3953
4251
|
await entity.put({
|
|
3954
4252
|
sk: DATA_ENTITY_SK,
|
|
3955
4253
|
tenantId,
|
|
3956
4254
|
workspaceId,
|
|
3957
4255
|
id,
|
|
3958
4256
|
resource: compressResource(JSON.stringify(resourceWithAudit)),
|
|
4257
|
+
summary,
|
|
3959
4258
|
vid,
|
|
3960
|
-
lastUpdated
|
|
4259
|
+
lastUpdated,
|
|
4260
|
+
gsi1sk
|
|
3961
4261
|
}).go();
|
|
3962
4262
|
return {
|
|
3963
4263
|
id,
|
|
@@ -4004,6 +4304,9 @@ async function updateDataEntityById(entity, tenantId, workspaceId, id, resourceL
|
|
|
4004
4304
|
}
|
|
4005
4305
|
const existingStr = decompressResource(existing.data.resource);
|
|
4006
4306
|
const { resource, lastUpdated } = buildPatched(existingStr);
|
|
4307
|
+
const resourceLike = resource;
|
|
4308
|
+
const summary = JSON.stringify((0, import_types13.extractSummary)(resourceLike));
|
|
4309
|
+
const gsi1sk = (0, import_types13.extractSortKey)(resourceLike);
|
|
4007
4310
|
await entity.patch({
|
|
4008
4311
|
tenantId,
|
|
4009
4312
|
workspaceId,
|
|
@@ -4011,7 +4314,9 @@ async function updateDataEntityById(entity, tenantId, workspaceId, id, resourceL
|
|
|
4011
4314
|
sk: DATA_ENTITY_SK
|
|
4012
4315
|
}).set({
|
|
4013
4316
|
resource: compressResource(JSON.stringify(resource)),
|
|
4014
|
-
|
|
4317
|
+
summary,
|
|
4318
|
+
lastUpdated,
|
|
4319
|
+
gsi1sk
|
|
4015
4320
|
}).go();
|
|
4016
4321
|
return {
|
|
4017
4322
|
id,
|
|
@@ -12655,9 +12960,165 @@ async function listEncountersOperation(params) {
|
|
|
12655
12960
|
);
|
|
12656
12961
|
}
|
|
12657
12962
|
|
|
12963
|
+
// src/data/postgres/data-api-postgres-query-runner.ts
|
|
12964
|
+
var import_client_rds_data = require("@aws-sdk/client-rds-data");
|
|
12965
|
+
var DataApiPostgresQueryRunner = class {
|
|
12966
|
+
constructor(options) {
|
|
12967
|
+
this.client = options.client ?? new import_client_rds_data.RDSDataClient({});
|
|
12968
|
+
this.clusterArn = options.clusterArn;
|
|
12969
|
+
this.secretArn = options.secretArn;
|
|
12970
|
+
this.database = options.database;
|
|
12971
|
+
this.schema = options.schema;
|
|
12972
|
+
}
|
|
12973
|
+
async query(sql, params) {
|
|
12974
|
+
const out = await this.client.send(
|
|
12975
|
+
new import_client_rds_data.ExecuteStatementCommand({
|
|
12976
|
+
resourceArn: this.clusterArn,
|
|
12977
|
+
secretArn: this.secretArn,
|
|
12978
|
+
database: this.database,
|
|
12979
|
+
schema: this.schema,
|
|
12980
|
+
sql,
|
|
12981
|
+
parameters: params.map(toSqlParameter),
|
|
12982
|
+
// Results as named columns so we can map them back to JS objects.
|
|
12983
|
+
includeResultMetadata: true,
|
|
12984
|
+
// Encode JSONB results as strings, then parse client-side. Without
|
|
12985
|
+
// this, Data API returns JSON values inline as their underlying types
|
|
12986
|
+
// and complex JSONB columns get clipped.
|
|
12987
|
+
formatRecordsAs: "JSON"
|
|
12988
|
+
})
|
|
12989
|
+
);
|
|
12990
|
+
if (!out.formattedRecords) {
|
|
12991
|
+
return [];
|
|
12992
|
+
}
|
|
12993
|
+
return JSON.parse(out.formattedRecords);
|
|
12994
|
+
}
|
|
12995
|
+
};
|
|
12996
|
+
function toSqlParameter(param) {
|
|
12997
|
+
if (param.value === null) {
|
|
12998
|
+
return { name: param.name, value: { isNull: true } };
|
|
12999
|
+
}
|
|
13000
|
+
const v = param.value;
|
|
13001
|
+
let field;
|
|
13002
|
+
if (typeof v === "string") {
|
|
13003
|
+
field = { stringValue: v };
|
|
13004
|
+
} else if (typeof v === "boolean") {
|
|
13005
|
+
field = { booleanValue: v };
|
|
13006
|
+
} else if (Number.isInteger(v)) {
|
|
13007
|
+
field = { longValue: v };
|
|
13008
|
+
} else {
|
|
13009
|
+
field = { doubleValue: v };
|
|
13010
|
+
}
|
|
13011
|
+
return { name: param.name, value: field };
|
|
13012
|
+
}
|
|
13013
|
+
|
|
13014
|
+
// src/data/postgres/default-postgres-query-runner.ts
|
|
13015
|
+
var cached;
|
|
13016
|
+
function readEnv(name) {
|
|
13017
|
+
const v = process.env[name]?.trim();
|
|
13018
|
+
if (!v) {
|
|
13019
|
+
throw new Error(
|
|
13020
|
+
`Missing required env var for default PostgresQueryRunner: ${name}`
|
|
13021
|
+
);
|
|
13022
|
+
}
|
|
13023
|
+
return v;
|
|
13024
|
+
}
|
|
13025
|
+
function getDefaultPostgresQueryRunner() {
|
|
13026
|
+
if (!cached) {
|
|
13027
|
+
cached = new DataApiPostgresQueryRunner({
|
|
13028
|
+
clusterArn: readEnv("OPENHI_PG_CLUSTER_ARN"),
|
|
13029
|
+
secretArn: readEnv("OPENHI_PG_SECRET_ARN"),
|
|
13030
|
+
database: readEnv("OPENHI_PG_DATABASE"),
|
|
13031
|
+
schema: readEnv("OPENHI_PG_SCHEMA")
|
|
13032
|
+
});
|
|
13033
|
+
}
|
|
13034
|
+
return cached;
|
|
13035
|
+
}
|
|
13036
|
+
|
|
13037
|
+
// src/data/operations/data/encounter/encounter-search-by-patient-operation.ts
|
|
13038
|
+
var DEFAULT_LIMIT = 100;
|
|
13039
|
+
function buildOpenHiResourceUrn(opts) {
|
|
13040
|
+
return `urn:ohi:${opts.tenantId}:${opts.workspaceId}:${opts.resourceType}:${opts.resourceId}`;
|
|
13041
|
+
}
|
|
13042
|
+
function buildSearchEncountersByPatientSql() {
|
|
13043
|
+
return [
|
|
13044
|
+
"SELECT resource_id AS id, resource",
|
|
13045
|
+
"FROM resources",
|
|
13046
|
+
"WHERE tenant_id = :tenantId",
|
|
13047
|
+
" AND workspace_id = :workspaceId",
|
|
13048
|
+
" AND resource_type = 'Encounter'",
|
|
13049
|
+
" AND deleted_at IS NULL",
|
|
13050
|
+
" AND (resource @> :containmentRelative::jsonb",
|
|
13051
|
+
" OR resource @> :containmentUrn::jsonb)",
|
|
13052
|
+
"ORDER BY last_updated DESC",
|
|
13053
|
+
"LIMIT :limit;"
|
|
13054
|
+
].join("\n");
|
|
13055
|
+
}
|
|
13056
|
+
async function searchEncountersByPatientOperation(params) {
|
|
13057
|
+
const { context, patientId } = params;
|
|
13058
|
+
const { tenantId, workspaceId } = context;
|
|
13059
|
+
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
13060
|
+
const limit = params.limit ?? DEFAULT_LIMIT;
|
|
13061
|
+
const containmentRelative = JSON.stringify({
|
|
13062
|
+
subject: { reference: `Patient/${patientId}` }
|
|
13063
|
+
});
|
|
13064
|
+
const containmentUrn = JSON.stringify({
|
|
13065
|
+
subject: {
|
|
13066
|
+
reference: buildOpenHiResourceUrn({
|
|
13067
|
+
tenantId,
|
|
13068
|
+
workspaceId,
|
|
13069
|
+
resourceType: "Patient",
|
|
13070
|
+
resourceId: patientId
|
|
13071
|
+
})
|
|
13072
|
+
}
|
|
13073
|
+
});
|
|
13074
|
+
const rows = await runner.query(
|
|
13075
|
+
buildSearchEncountersByPatientSql(),
|
|
13076
|
+
[
|
|
13077
|
+
{ name: "tenantId", value: tenantId },
|
|
13078
|
+
{ name: "workspaceId", value: workspaceId },
|
|
13079
|
+
{ name: "containmentRelative", value: containmentRelative },
|
|
13080
|
+
{ name: "containmentUrn", value: containmentUrn },
|
|
13081
|
+
{ name: "limit", value: limit }
|
|
13082
|
+
]
|
|
13083
|
+
);
|
|
13084
|
+
const entries = rows.map((row) => ({
|
|
13085
|
+
id: row.id,
|
|
13086
|
+
resource: {
|
|
13087
|
+
...row.resource,
|
|
13088
|
+
id: row.id
|
|
13089
|
+
}
|
|
13090
|
+
}));
|
|
13091
|
+
return { entries };
|
|
13092
|
+
}
|
|
13093
|
+
|
|
12658
13094
|
// src/data/rest-api/routes/data/encounter/encounter-list-route.ts
|
|
13095
|
+
function singleStringQueryParam(req, name) {
|
|
13096
|
+
const v = req.query[name];
|
|
13097
|
+
if (typeof v !== "string") {
|
|
13098
|
+
return void 0;
|
|
13099
|
+
}
|
|
13100
|
+
const trimmed = v.trim();
|
|
13101
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
13102
|
+
}
|
|
12659
13103
|
async function listEncountersRoute(req, res) {
|
|
12660
13104
|
const ctx = req.openhiContext;
|
|
13105
|
+
const patientId = singleStringQueryParam(req, "patient");
|
|
13106
|
+
if (patientId) {
|
|
13107
|
+
try {
|
|
13108
|
+
const result = await searchEncountersByPatientOperation({
|
|
13109
|
+
context: ctx,
|
|
13110
|
+
patientId
|
|
13111
|
+
});
|
|
13112
|
+
const bundle = buildSearchsetBundle(BASE_PATH.ENCOUNTER, result.entries);
|
|
13113
|
+
return res.json(bundle);
|
|
13114
|
+
} catch (err) {
|
|
13115
|
+
return sendOperationOutcome500(
|
|
13116
|
+
res,
|
|
13117
|
+
err,
|
|
13118
|
+
"GET /Encounter?patient= search error:"
|
|
13119
|
+
);
|
|
13120
|
+
}
|
|
13121
|
+
}
|
|
12661
13122
|
try {
|
|
12662
13123
|
const result = await listEncountersOperation({ context: ctx });
|
|
12663
13124
|
const bundle = buildSearchsetBundle(BASE_PATH.ENCOUNTER, result.entries);
|
|
@@ -24574,6 +25035,7 @@ var import_ulid99 = require("ulid");
|
|
|
24574
25035
|
// src/data/import-patient.ts
|
|
24575
25036
|
var import_node_fs = require("fs");
|
|
24576
25037
|
var import_node_path = require("path");
|
|
25038
|
+
var import_types14 = require("@openhi/types");
|
|
24577
25039
|
function extractPatient(parsed) {
|
|
24578
25040
|
if (parsed && typeof parsed === "object" && "resourceType" in parsed) {
|
|
24579
25041
|
const root = parsed;
|
|
@@ -24639,6 +25101,9 @@ function patientToPutAttrs(patient, options) {
|
|
|
24639
25101
|
workspaceId,
|
|
24640
25102
|
id: patient.id,
|
|
24641
25103
|
resource: compressResource(JSON.stringify(patientWithMeta)),
|
|
25104
|
+
summary: JSON.stringify(
|
|
25105
|
+
(0, import_types14.extractSummary)(patientWithMeta)
|
|
25106
|
+
),
|
|
24642
25107
|
vid: lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36),
|
|
24643
25108
|
lastUpdated,
|
|
24644
25109
|
identifierSystem: "",
|
|
@@ -33959,6 +34424,7 @@ app.use(
|
|
|
33959
34424
|
"/MedicinalProductPackaged",
|
|
33960
34425
|
"/MedicinalProductPharmaceutical",
|
|
33961
34426
|
"/MedicinalProductUndesirableEffect",
|
|
34427
|
+
"/Membership",
|
|
33962
34428
|
"/MessageDefinition",
|
|
33963
34429
|
"/MessageHeader",
|
|
33964
34430
|
"/MolecularSequence",
|
|
@@ -33988,6 +34454,8 @@ app.use(
|
|
|
33988
34454
|
"/ResearchSubject",
|
|
33989
34455
|
"/RiskAssessment",
|
|
33990
34456
|
"/RiskEvidenceSynthesis",
|
|
34457
|
+
"/Role",
|
|
34458
|
+
"/RoleAssignment",
|
|
33991
34459
|
"/Schedule",
|
|
33992
34460
|
"/ServiceRequest",
|
|
33993
34461
|
"/SearchParameter",
|
|
@@ -34007,12 +34475,15 @@ app.use(
|
|
|
34007
34475
|
"/SupplyDelivery",
|
|
34008
34476
|
"/SupplyRequest",
|
|
34009
34477
|
"/Task",
|
|
34478
|
+
"/Tenant",
|
|
34010
34479
|
"/TerminologyCapabilities",
|
|
34011
34480
|
"/TestReport",
|
|
34012
34481
|
"/TestScript",
|
|
34482
|
+
"/User",
|
|
34013
34483
|
"/ValueSet",
|
|
34014
34484
|
"/VerificationResult",
|
|
34015
|
-
"/VisionPrescription"
|
|
34485
|
+
"/VisionPrescription",
|
|
34486
|
+
"/Workspace"
|
|
34016
34487
|
],
|
|
34017
34488
|
openHiContextMiddleware
|
|
34018
34489
|
);
|
|
@@ -34174,6 +34645,18 @@ app.use("/RoleAssignment", router4);
|
|
|
34174
34645
|
app.use("/Tenant", router5);
|
|
34175
34646
|
app.use("/User", router6);
|
|
34176
34647
|
app.use("/Workspace", router7);
|
|
34648
|
+
app.use((_req, res) => {
|
|
34649
|
+
res.status(404).json({
|
|
34650
|
+
resourceType: "OperationOutcome",
|
|
34651
|
+
issue: [
|
|
34652
|
+
{
|
|
34653
|
+
severity: "error",
|
|
34654
|
+
code: "not-supported",
|
|
34655
|
+
diagnostics: "The requested endpoint or resource type is not supported by this server."
|
|
34656
|
+
}
|
|
34657
|
+
]
|
|
34658
|
+
});
|
|
34659
|
+
});
|
|
34177
34660
|
|
|
34178
34661
|
// src/data/lambda/rest-api-lambda.handler.ts
|
|
34179
34662
|
var handler = (0, import_serverless_express2.default)({ app });
|