@openhi/constructs 0.0.90 → 0.0.92
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-LZOMFHX3.mjs → chunk-3QS3WKRC.mjs} +2 -9
- package/lib/chunk-MLTYFMSE.mjs +807 -0
- package/lib/chunk-MLTYFMSE.mjs.map +1 -0
- package/lib/cors-options-lambda.handler.mjs +1 -1
- package/lib/data-store-postgres-replication.handler.mjs +1 -1
- package/lib/firehose-archive-transform.handler.mjs +1 -1
- package/lib/index.d.mts +42 -1
- package/lib/index.d.ts +43 -2
- package/lib/index.js +280 -181
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +282 -184
- package/lib/index.mjs.map +1 -1
- package/lib/post-authentication.handler.mjs +1 -1
- package/lib/post-confirmation.handler.d.mts +5 -0
- package/lib/post-confirmation.handler.d.ts +5 -0
- package/lib/post-confirmation.handler.js +949 -0
- package/lib/post-confirmation.handler.js.map +1 -0
- package/lib/post-confirmation.handler.mjs +128 -0
- package/lib/post-confirmation.handler.mjs.map +1 -0
- package/lib/pre-token-generation.handler.mjs +1 -1
- package/lib/rest-api-lambda.handler.js +19 -145
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +29 -949
- package/lib/rest-api-lambda.handler.mjs.map +1 -1
- package/package.json +3 -3
- /package/lib/{chunk-LZOMFHX3.mjs.map → chunk-3QS3WKRC.mjs.map} +0 -0
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
SHARD_COUNT,
|
|
3
|
+
computeShard,
|
|
4
|
+
defaultTableName,
|
|
5
|
+
dynamoClient,
|
|
6
|
+
getDynamoControlService
|
|
7
|
+
} from "./chunk-MLTYFMSE.mjs";
|
|
8
|
+
import "./chunk-3QS3WKRC.mjs";
|
|
4
9
|
|
|
5
10
|
// src/data/lambda/rest-api-lambda.handler.ts
|
|
6
11
|
import serverlessExpress from "@codegenie/serverless-express";
|
|
@@ -126,805 +131,6 @@ function decompressResource(compressedOrRaw) {
|
|
|
126
131
|
return compressedOrRaw;
|
|
127
132
|
}
|
|
128
133
|
|
|
129
|
-
// src/data/dynamo/dynamo-control-service.ts
|
|
130
|
-
import { Service } from "electrodb";
|
|
131
|
-
|
|
132
|
-
// src/data/dynamo/dynamo-client.ts
|
|
133
|
-
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
134
|
-
var defaultTableName = process.env.DYNAMO_TABLE_NAME ?? "jesttesttable";
|
|
135
|
-
var dynamoClient = new DynamoDBClient({
|
|
136
|
-
...process.env.MOCK_DYNAMODB_ENDPOINT && {
|
|
137
|
-
endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
|
|
138
|
-
sslEnabled: false,
|
|
139
|
-
region: "local"
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// src/data/dynamo/entities/control/configuration-entity.ts
|
|
144
|
-
import { Entity } from "electrodb";
|
|
145
|
-
|
|
146
|
-
// src/data/dynamo/shard.ts
|
|
147
|
-
var SHARD_COUNT = 4;
|
|
148
|
-
function computeShard(id) {
|
|
149
|
-
let hash = 2166136261;
|
|
150
|
-
for (let i = 0; i < id.length; i++) {
|
|
151
|
-
hash ^= id.charCodeAt(i);
|
|
152
|
-
hash = Math.imul(hash, 16777619);
|
|
153
|
-
}
|
|
154
|
-
return (hash >>> 0) % SHARD_COUNT;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// src/data/dynamo/entities/control/control-entity-common.ts
|
|
158
|
-
var gsi1ShardAttribute = {
|
|
159
|
-
type: "string",
|
|
160
|
-
watch: ["id"],
|
|
161
|
-
set: (_val, item) => {
|
|
162
|
-
if (typeof item?.id !== "string" || item.id.length === 0) {
|
|
163
|
-
return void 0;
|
|
164
|
-
}
|
|
165
|
-
return String(computeShard(item.id));
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
// src/data/dynamo/entities/control/configuration-entity.ts
|
|
170
|
-
var ConfigurationEntity = new Entity({
|
|
171
|
-
model: {
|
|
172
|
-
entity: "configuration",
|
|
173
|
-
service: "control",
|
|
174
|
-
version: "01"
|
|
175
|
-
},
|
|
176
|
-
attributes: {
|
|
177
|
-
/** Sort key. "CURRENT" for current version; version history in S3. */
|
|
178
|
-
sk: {
|
|
179
|
-
type: "string",
|
|
180
|
-
required: true,
|
|
181
|
-
default: "CURRENT"
|
|
182
|
-
},
|
|
183
|
-
/** Tenant scope. Use "BASELINE" when the config is baseline default (no tenant). */
|
|
184
|
-
tenantId: {
|
|
185
|
-
type: "string",
|
|
186
|
-
required: true,
|
|
187
|
-
default: "BASELINE"
|
|
188
|
-
},
|
|
189
|
-
/** Workspace scope. Use "-" when absent. */
|
|
190
|
-
workspaceId: {
|
|
191
|
-
type: "string",
|
|
192
|
-
required: true,
|
|
193
|
-
default: "-"
|
|
194
|
-
},
|
|
195
|
-
/** User scope. Use "-" when absent. */
|
|
196
|
-
userId: {
|
|
197
|
-
type: "string",
|
|
198
|
-
required: true,
|
|
199
|
-
default: "-"
|
|
200
|
-
},
|
|
201
|
-
/** Role scope. Use "-" when absent. */
|
|
202
|
-
roleId: {
|
|
203
|
-
type: "string",
|
|
204
|
-
required: true,
|
|
205
|
-
default: "-"
|
|
206
|
-
},
|
|
207
|
-
/** Config type (category), e.g. endpoints, branding, display. */
|
|
208
|
-
key: {
|
|
209
|
-
type: "string",
|
|
210
|
-
required: true
|
|
211
|
-
},
|
|
212
|
-
/** FHIR Resource.id; logical id in URL and for the Configuration resource. */
|
|
213
|
-
id: {
|
|
214
|
-
type: "string",
|
|
215
|
-
required: true
|
|
216
|
-
},
|
|
217
|
-
/** Payload as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */
|
|
218
|
-
resource: {
|
|
219
|
-
type: "string",
|
|
220
|
-
required: true
|
|
221
|
-
},
|
|
222
|
-
/**
|
|
223
|
-
* Summary projection (key display fields as JSON string: id, key, status).
|
|
224
|
-
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
225
|
-
*/
|
|
226
|
-
summary: {
|
|
227
|
-
type: "string",
|
|
228
|
-
required: true
|
|
229
|
-
},
|
|
230
|
-
/** Version id (e.g. ULID). Tracks current version; S3 history key. */
|
|
231
|
-
vid: {
|
|
232
|
-
type: "string",
|
|
233
|
-
required: true
|
|
234
|
-
},
|
|
235
|
-
lastUpdated: {
|
|
236
|
-
type: "string",
|
|
237
|
-
required: true
|
|
238
|
-
},
|
|
239
|
-
gsi1Shard: gsi1ShardAttribute,
|
|
240
|
-
deleted: {
|
|
241
|
-
type: "boolean",
|
|
242
|
-
required: false
|
|
243
|
-
},
|
|
244
|
-
bundleId: {
|
|
245
|
-
type: "string",
|
|
246
|
-
required: false
|
|
247
|
-
},
|
|
248
|
-
msgId: {
|
|
249
|
-
type: "string",
|
|
250
|
-
required: false
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
indexes: {
|
|
254
|
-
/** Base table: PK, SK (data store key names). PK is built from tenantId, workspaceId, userId, roleId; SK is built from key and sk. Do not supply PK or SK from outside. */
|
|
255
|
-
record: {
|
|
256
|
-
pk: {
|
|
257
|
-
field: "PK",
|
|
258
|
-
composite: ["tenantId", "workspaceId", "userId", "roleId"],
|
|
259
|
-
template: "CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}"
|
|
260
|
-
},
|
|
261
|
-
sk: {
|
|
262
|
-
field: "SK",
|
|
263
|
-
composite: ["key", "sk"],
|
|
264
|
-
template: "KEY#${key}#SK#${sk}"
|
|
265
|
-
}
|
|
266
|
-
},
|
|
267
|
-
/**
|
|
268
|
-
* GSI1 — Unified Sharded List per ADR-011: list all Configuration entries for a
|
|
269
|
-
* (tenant, workspace) across the four shards. Use for "list configs scoped to this tenant"
|
|
270
|
-
* (workspaceId = "-") or "list configs scoped to this workspace". Does not support
|
|
271
|
-
* hierarchical resolution in one query; use base table GetItem in fallback order
|
|
272
|
-
* (user → workspace → tenant → baseline) for that.
|
|
273
|
-
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
274
|
-
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
275
|
-
*/
|
|
276
|
-
gsi1: {
|
|
277
|
-
index: "GSI1",
|
|
278
|
-
pk: {
|
|
279
|
-
field: "GSI1PK",
|
|
280
|
-
composite: ["tenantId", "workspaceId", "gsi1Shard"],
|
|
281
|
-
template: "TID#${tenantId}#WID#${workspaceId}#RT#Configuration#SHARD#${gsi1Shard}"
|
|
282
|
-
},
|
|
283
|
-
sk: {
|
|
284
|
-
field: "GSI1SK",
|
|
285
|
-
casing: "none",
|
|
286
|
-
composite: ["lastUpdated", "id"],
|
|
287
|
-
template: "${lastUpdated}#${id}"
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// src/data/dynamo/entities/control/membership-entity.ts
|
|
294
|
-
import { Entity as Entity2 } from "electrodb";
|
|
295
|
-
var MembershipEntity = new Entity2({
|
|
296
|
-
model: {
|
|
297
|
-
entity: "membership",
|
|
298
|
-
service: "control",
|
|
299
|
-
version: "01"
|
|
300
|
-
},
|
|
301
|
-
attributes: {
|
|
302
|
-
/** Sort key sentinel. Always "CURRENT". */
|
|
303
|
-
sk: {
|
|
304
|
-
type: "string",
|
|
305
|
-
required: true,
|
|
306
|
-
default: "CURRENT"
|
|
307
|
-
},
|
|
308
|
-
/** Tenant in which the user has membership (required). */
|
|
309
|
-
tenantId: {
|
|
310
|
-
type: "string",
|
|
311
|
-
required: true
|
|
312
|
-
},
|
|
313
|
-
/** FHIR Resource.id; membership id. */
|
|
314
|
-
id: {
|
|
315
|
-
type: "string",
|
|
316
|
-
required: true
|
|
317
|
-
},
|
|
318
|
-
/** Full Membership resource serialized as JSON string. */
|
|
319
|
-
resource: {
|
|
320
|
-
type: "string",
|
|
321
|
-
required: true
|
|
322
|
-
},
|
|
323
|
-
/**
|
|
324
|
-
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
325
|
-
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
326
|
-
*/
|
|
327
|
-
summary: {
|
|
328
|
-
type: "string",
|
|
329
|
-
required: true
|
|
330
|
-
},
|
|
331
|
-
/** Version id (e.g. ULID). */
|
|
332
|
-
vid: {
|
|
333
|
-
type: "string",
|
|
334
|
-
required: true
|
|
335
|
-
},
|
|
336
|
-
lastUpdated: {
|
|
337
|
-
type: "string",
|
|
338
|
-
required: true
|
|
339
|
-
},
|
|
340
|
-
gsi1Shard: gsi1ShardAttribute,
|
|
341
|
-
deleted: {
|
|
342
|
-
type: "boolean",
|
|
343
|
-
required: false
|
|
344
|
-
},
|
|
345
|
-
bundleId: {
|
|
346
|
-
type: "string",
|
|
347
|
-
required: false
|
|
348
|
-
},
|
|
349
|
-
msgId: {
|
|
350
|
-
type: "string",
|
|
351
|
-
required: false
|
|
352
|
-
}
|
|
353
|
-
},
|
|
354
|
-
indexes: {
|
|
355
|
-
/** Base table: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
356
|
-
record: {
|
|
357
|
-
pk: {
|
|
358
|
-
field: "PK",
|
|
359
|
-
composite: ["tenantId", "id"],
|
|
360
|
-
template: "TID#${tenantId}#MEMBERSHIP#ID#${id}"
|
|
361
|
-
},
|
|
362
|
-
sk: {
|
|
363
|
-
field: "SK",
|
|
364
|
-
composite: ["sk"],
|
|
365
|
-
template: "${sk}"
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
/**
|
|
369
|
-
* GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the
|
|
370
|
-
* four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.
|
|
371
|
-
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
372
|
-
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
373
|
-
*/
|
|
374
|
-
gsi1: {
|
|
375
|
-
index: "GSI1",
|
|
376
|
-
pk: {
|
|
377
|
-
field: "GSI1PK",
|
|
378
|
-
composite: ["tenantId", "gsi1Shard"],
|
|
379
|
-
template: "TID#${tenantId}#WID#-#RT#Membership#SHARD#${gsi1Shard}"
|
|
380
|
-
},
|
|
381
|
-
sk: {
|
|
382
|
-
field: "GSI1SK",
|
|
383
|
-
casing: "none",
|
|
384
|
-
composite: ["lastUpdated", "id"],
|
|
385
|
-
template: "${lastUpdated}#${id}"
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
// src/data/dynamo/entities/control/role-entity.ts
|
|
392
|
-
import { Entity as Entity3 } from "electrodb";
|
|
393
|
-
var RoleEntity = new Entity3({
|
|
394
|
-
model: {
|
|
395
|
-
entity: "role",
|
|
396
|
-
service: "control",
|
|
397
|
-
version: "01"
|
|
398
|
-
},
|
|
399
|
-
attributes: {
|
|
400
|
-
/** Sort key sentinel. Always "CURRENT". */
|
|
401
|
-
sk: {
|
|
402
|
-
type: "string",
|
|
403
|
-
required: true,
|
|
404
|
-
default: "CURRENT"
|
|
405
|
-
},
|
|
406
|
-
/** FHIR Resource.id; role id. */
|
|
407
|
-
id: {
|
|
408
|
-
type: "string",
|
|
409
|
-
required: true
|
|
410
|
-
},
|
|
411
|
-
/** Full Role resource serialized as JSON string. */
|
|
412
|
-
resource: {
|
|
413
|
-
type: "string",
|
|
414
|
-
required: true
|
|
415
|
-
},
|
|
416
|
-
/**
|
|
417
|
-
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
418
|
-
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
419
|
-
*/
|
|
420
|
-
summary: {
|
|
421
|
-
type: "string",
|
|
422
|
-
required: true
|
|
423
|
-
},
|
|
424
|
-
/** Version id (e.g. ULID). */
|
|
425
|
-
vid: {
|
|
426
|
-
type: "string",
|
|
427
|
-
required: true
|
|
428
|
-
},
|
|
429
|
-
lastUpdated: {
|
|
430
|
-
type: "string",
|
|
431
|
-
required: true
|
|
432
|
-
},
|
|
433
|
-
gsi1Shard: gsi1ShardAttribute,
|
|
434
|
-
deleted: {
|
|
435
|
-
type: "boolean",
|
|
436
|
-
required: false
|
|
437
|
-
},
|
|
438
|
-
bundleId: {
|
|
439
|
-
type: "string",
|
|
440
|
-
required: false
|
|
441
|
-
},
|
|
442
|
-
msgId: {
|
|
443
|
-
type: "string",
|
|
444
|
-
required: false
|
|
445
|
-
}
|
|
446
|
-
},
|
|
447
|
-
indexes: {
|
|
448
|
-
/** Base table: PK = ROLE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
449
|
-
record: {
|
|
450
|
-
pk: {
|
|
451
|
-
field: "PK",
|
|
452
|
-
composite: ["id"],
|
|
453
|
-
template: "ROLE#ID#${id}"
|
|
454
|
-
},
|
|
455
|
-
sk: {
|
|
456
|
-
field: "SK",
|
|
457
|
-
composite: ["sk"],
|
|
458
|
-
template: "${sk}"
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
/**
|
|
462
|
-
* GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.
|
|
463
|
-
* Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.
|
|
464
|
-
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
465
|
-
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
466
|
-
*/
|
|
467
|
-
gsi1: {
|
|
468
|
-
index: "GSI1",
|
|
469
|
-
pk: {
|
|
470
|
-
field: "GSI1PK",
|
|
471
|
-
composite: ["gsi1Shard"],
|
|
472
|
-
template: "TID#-#WID#-#RT#Role#SHARD#${gsi1Shard}"
|
|
473
|
-
},
|
|
474
|
-
sk: {
|
|
475
|
-
field: "GSI1SK",
|
|
476
|
-
casing: "none",
|
|
477
|
-
composite: ["lastUpdated", "id"],
|
|
478
|
-
template: "${lastUpdated}#${id}"
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
// src/data/dynamo/entities/control/roleassignment-entity.ts
|
|
485
|
-
import { Entity as Entity4 } from "electrodb";
|
|
486
|
-
var RoleAssignmentEntity = new Entity4({
|
|
487
|
-
model: {
|
|
488
|
-
entity: "roleassignment",
|
|
489
|
-
service: "control",
|
|
490
|
-
version: "01"
|
|
491
|
-
},
|
|
492
|
-
attributes: {
|
|
493
|
-
/** Sort key sentinel. Always "CURRENT". */
|
|
494
|
-
sk: {
|
|
495
|
-
type: "string",
|
|
496
|
-
required: true,
|
|
497
|
-
default: "CURRENT"
|
|
498
|
-
},
|
|
499
|
-
/** Tenant in which the role assignment applies (required). */
|
|
500
|
-
tenantId: {
|
|
501
|
-
type: "string",
|
|
502
|
-
required: true
|
|
503
|
-
},
|
|
504
|
-
/** FHIR Resource.id; role assignment id. */
|
|
505
|
-
id: {
|
|
506
|
-
type: "string",
|
|
507
|
-
required: true
|
|
508
|
-
},
|
|
509
|
-
/** Full RoleAssignment resource serialized as JSON string. */
|
|
510
|
-
resource: {
|
|
511
|
-
type: "string",
|
|
512
|
-
required: true
|
|
513
|
-
},
|
|
514
|
-
/**
|
|
515
|
-
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
516
|
-
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
517
|
-
*/
|
|
518
|
-
summary: {
|
|
519
|
-
type: "string",
|
|
520
|
-
required: true
|
|
521
|
-
},
|
|
522
|
-
/** Version id (e.g. ULID). */
|
|
523
|
-
vid: {
|
|
524
|
-
type: "string",
|
|
525
|
-
required: true
|
|
526
|
-
},
|
|
527
|
-
lastUpdated: {
|
|
528
|
-
type: "string",
|
|
529
|
-
required: true
|
|
530
|
-
},
|
|
531
|
-
gsi1Shard: gsi1ShardAttribute,
|
|
532
|
-
deleted: {
|
|
533
|
-
type: "boolean",
|
|
534
|
-
required: false
|
|
535
|
-
},
|
|
536
|
-
bundleId: {
|
|
537
|
-
type: "string",
|
|
538
|
-
required: false
|
|
539
|
-
},
|
|
540
|
-
msgId: {
|
|
541
|
-
type: "string",
|
|
542
|
-
required: false
|
|
543
|
-
}
|
|
544
|
-
},
|
|
545
|
-
indexes: {
|
|
546
|
-
/** Base table: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
547
|
-
record: {
|
|
548
|
-
pk: {
|
|
549
|
-
field: "PK",
|
|
550
|
-
composite: ["tenantId", "id"],
|
|
551
|
-
template: "TID#${tenantId}#ROLEASSIGNMENT#ID#${id}"
|
|
552
|
-
},
|
|
553
|
-
sk: {
|
|
554
|
-
field: "SK",
|
|
555
|
-
composite: ["sk"],
|
|
556
|
-
template: "${sk}"
|
|
557
|
-
}
|
|
558
|
-
},
|
|
559
|
-
/**
|
|
560
|
-
* GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the
|
|
561
|
-
* four shards. Tenant-scoped only, so `WID#-` is a sentinel.
|
|
562
|
-
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
563
|
-
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
564
|
-
*/
|
|
565
|
-
gsi1: {
|
|
566
|
-
index: "GSI1",
|
|
567
|
-
pk: {
|
|
568
|
-
field: "GSI1PK",
|
|
569
|
-
composite: ["tenantId", "gsi1Shard"],
|
|
570
|
-
template: "TID#${tenantId}#WID#-#RT#RoleAssignment#SHARD#${gsi1Shard}"
|
|
571
|
-
},
|
|
572
|
-
sk: {
|
|
573
|
-
field: "GSI1SK",
|
|
574
|
-
casing: "none",
|
|
575
|
-
composite: ["lastUpdated", "id"],
|
|
576
|
-
template: "${lastUpdated}#${id}"
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
// src/data/dynamo/entities/control/tenant-entity.ts
|
|
583
|
-
import { Entity as Entity5 } from "electrodb";
|
|
584
|
-
var TenantEntity = new Entity5({
|
|
585
|
-
model: {
|
|
586
|
-
entity: "tenant",
|
|
587
|
-
service: "control",
|
|
588
|
-
version: "01"
|
|
589
|
-
},
|
|
590
|
-
attributes: {
|
|
591
|
-
/** Sort key sentinel. Always "CURRENT". */
|
|
592
|
-
sk: {
|
|
593
|
-
type: "string",
|
|
594
|
-
required: true,
|
|
595
|
-
default: "CURRENT"
|
|
596
|
-
},
|
|
597
|
-
/** The tenant's own id (= resource id). Drives the partition key. */
|
|
598
|
-
tenantId: {
|
|
599
|
-
type: "string",
|
|
600
|
-
required: true
|
|
601
|
-
},
|
|
602
|
-
/** FHIR Resource.id; logical id in URL. Equals tenantId. */
|
|
603
|
-
id: {
|
|
604
|
-
type: "string",
|
|
605
|
-
required: true
|
|
606
|
-
},
|
|
607
|
-
/** Full Tenant resource serialized as JSON string. */
|
|
608
|
-
resource: {
|
|
609
|
-
type: "string",
|
|
610
|
-
required: true
|
|
611
|
-
},
|
|
612
|
-
/**
|
|
613
|
-
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
614
|
-
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
615
|
-
*/
|
|
616
|
-
summary: {
|
|
617
|
-
type: "string",
|
|
618
|
-
required: true
|
|
619
|
-
},
|
|
620
|
-
/** Version id (e.g. ULID). */
|
|
621
|
-
vid: {
|
|
622
|
-
type: "string",
|
|
623
|
-
required: true
|
|
624
|
-
},
|
|
625
|
-
lastUpdated: {
|
|
626
|
-
type: "string",
|
|
627
|
-
required: true
|
|
628
|
-
},
|
|
629
|
-
gsi1Shard: gsi1ShardAttribute,
|
|
630
|
-
deleted: {
|
|
631
|
-
type: "boolean",
|
|
632
|
-
required: false
|
|
633
|
-
},
|
|
634
|
-
bundleId: {
|
|
635
|
-
type: "string",
|
|
636
|
-
required: false
|
|
637
|
-
},
|
|
638
|
-
msgId: {
|
|
639
|
-
type: "string",
|
|
640
|
-
required: false
|
|
641
|
-
}
|
|
642
|
-
},
|
|
643
|
-
indexes: {
|
|
644
|
-
/** Base table: PK = TENANT#ID#<tenantId>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
645
|
-
record: {
|
|
646
|
-
pk: {
|
|
647
|
-
field: "PK",
|
|
648
|
-
composite: ["tenantId"],
|
|
649
|
-
template: "TENANT#ID#${tenantId}"
|
|
650
|
-
},
|
|
651
|
-
sk: {
|
|
652
|
-
field: "SK",
|
|
653
|
-
composite: ["sk"],
|
|
654
|
-
template: "${sk}"
|
|
655
|
-
}
|
|
656
|
-
},
|
|
657
|
-
/**
|
|
658
|
-
* GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.
|
|
659
|
-
* Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`
|
|
660
|
-
* sentinels precede `RT#Tenant#SHARD#<n>`. SK is `<ISO-8601 lastUpdated>#<id>` (control-plane
|
|
661
|
-
* unlabeled per DR-004). `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
662
|
-
*/
|
|
663
|
-
gsi1: {
|
|
664
|
-
index: "GSI1",
|
|
665
|
-
pk: {
|
|
666
|
-
field: "GSI1PK",
|
|
667
|
-
composite: ["gsi1Shard"],
|
|
668
|
-
template: "TID#-#WID#-#RT#Tenant#SHARD#${gsi1Shard}"
|
|
669
|
-
},
|
|
670
|
-
sk: {
|
|
671
|
-
field: "GSI1SK",
|
|
672
|
-
casing: "none",
|
|
673
|
-
composite: ["lastUpdated", "id"],
|
|
674
|
-
template: "${lastUpdated}#${id}"
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
// src/data/dynamo/entities/control/user-entity.ts
|
|
681
|
-
import { Entity as Entity6 } from "electrodb";
|
|
682
|
-
var UserEntity = new Entity6({
|
|
683
|
-
model: {
|
|
684
|
-
entity: "user",
|
|
685
|
-
service: "control",
|
|
686
|
-
version: "01"
|
|
687
|
-
},
|
|
688
|
-
attributes: {
|
|
689
|
-
/** Sort key sentinel. Always "CURRENT". */
|
|
690
|
-
sk: {
|
|
691
|
-
type: "string",
|
|
692
|
-
required: true,
|
|
693
|
-
default: "CURRENT"
|
|
694
|
-
},
|
|
695
|
-
/** FHIR Resource.id; platform user id (ohi_uid). */
|
|
696
|
-
id: {
|
|
697
|
-
type: "string",
|
|
698
|
-
required: true
|
|
699
|
-
},
|
|
700
|
-
/** Full User resource serialized as JSON string. */
|
|
701
|
-
resource: {
|
|
702
|
-
type: "string",
|
|
703
|
-
required: true
|
|
704
|
-
},
|
|
705
|
-
/**
|
|
706
|
-
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
707
|
-
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
708
|
-
*/
|
|
709
|
-
summary: {
|
|
710
|
-
type: "string",
|
|
711
|
-
required: true
|
|
712
|
-
},
|
|
713
|
-
/**
|
|
714
|
-
* Immutable Cognito-issued `sub` claim. Drives GSI2 (sub-lookup). Optional until the
|
|
715
|
-
* Post Confirmation Lambda (#770) lands; required thereafter.
|
|
716
|
-
*/
|
|
717
|
-
cognitoSub: {
|
|
718
|
-
type: "string",
|
|
719
|
-
required: false
|
|
720
|
-
},
|
|
721
|
-
/** Version id (e.g. ULID). */
|
|
722
|
-
vid: {
|
|
723
|
-
type: "string",
|
|
724
|
-
required: true
|
|
725
|
-
},
|
|
726
|
-
lastUpdated: {
|
|
727
|
-
type: "string",
|
|
728
|
-
required: true
|
|
729
|
-
},
|
|
730
|
-
gsi1Shard: gsi1ShardAttribute,
|
|
731
|
-
deleted: {
|
|
732
|
-
type: "boolean",
|
|
733
|
-
required: false
|
|
734
|
-
},
|
|
735
|
-
bundleId: {
|
|
736
|
-
type: "string",
|
|
737
|
-
required: false
|
|
738
|
-
},
|
|
739
|
-
msgId: {
|
|
740
|
-
type: "string",
|
|
741
|
-
required: false
|
|
742
|
-
}
|
|
743
|
-
},
|
|
744
|
-
indexes: {
|
|
745
|
-
/** Base table: PK = USER#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
746
|
-
record: {
|
|
747
|
-
pk: {
|
|
748
|
-
field: "PK",
|
|
749
|
-
composite: ["id"],
|
|
750
|
-
template: "USER#ID#${id}"
|
|
751
|
-
},
|
|
752
|
-
sk: {
|
|
753
|
-
field: "SK",
|
|
754
|
-
composite: ["sk"],
|
|
755
|
-
template: "${sk}"
|
|
756
|
-
}
|
|
757
|
-
},
|
|
758
|
-
/**
|
|
759
|
-
* GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.
|
|
760
|
-
* Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.
|
|
761
|
-
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
762
|
-
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z` characters.
|
|
763
|
-
*/
|
|
764
|
-
gsi1: {
|
|
765
|
-
index: "GSI1",
|
|
766
|
-
pk: {
|
|
767
|
-
field: "GSI1PK",
|
|
768
|
-
composite: ["gsi1Shard"],
|
|
769
|
-
template: "TID#-#WID#-#RT#User#SHARD#${gsi1Shard}"
|
|
770
|
-
},
|
|
771
|
-
sk: {
|
|
772
|
-
field: "GSI1SK",
|
|
773
|
-
casing: "none",
|
|
774
|
-
composite: ["lastUpdated", "id"],
|
|
775
|
-
template: "${lastUpdated}#${id}"
|
|
776
|
-
}
|
|
777
|
-
},
|
|
778
|
-
/**
|
|
779
|
-
* GSI2 — Cognito sub-lookup per ADR-011: resolves the UserEntity from a Cognito `sub` claim.
|
|
780
|
-
* `condition` skips the index when `cognitoSub` is missing so legacy items without a sub are
|
|
781
|
-
* not indexed.
|
|
782
|
-
*/
|
|
783
|
-
gsi2: {
|
|
784
|
-
index: "GSI2",
|
|
785
|
-
condition: (attrs) => typeof attrs.cognitoSub === "string" && attrs.cognitoSub.length > 0,
|
|
786
|
-
pk: {
|
|
787
|
-
field: "GSI2PK",
|
|
788
|
-
casing: "none",
|
|
789
|
-
composite: ["cognitoSub"],
|
|
790
|
-
template: "USER#SUB#${cognitoSub}"
|
|
791
|
-
},
|
|
792
|
-
sk: {
|
|
793
|
-
field: "GSI2SK",
|
|
794
|
-
casing: "none",
|
|
795
|
-
composite: [],
|
|
796
|
-
template: "CURRENT"
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
});
|
|
801
|
-
|
|
802
|
-
// src/data/dynamo/entities/control/workspace-entity.ts
|
|
803
|
-
import { Entity as Entity7 } from "electrodb";
|
|
804
|
-
var WorkspaceEntity = new Entity7({
|
|
805
|
-
model: {
|
|
806
|
-
entity: "workspace",
|
|
807
|
-
service: "control",
|
|
808
|
-
version: "01"
|
|
809
|
-
},
|
|
810
|
-
attributes: {
|
|
811
|
-
/** Sort key sentinel. Always "CURRENT". */
|
|
812
|
-
sk: {
|
|
813
|
-
type: "string",
|
|
814
|
-
required: true,
|
|
815
|
-
default: "CURRENT"
|
|
816
|
-
},
|
|
817
|
-
/** Tenant that contains this workspace (required). */
|
|
818
|
-
tenantId: {
|
|
819
|
-
type: "string",
|
|
820
|
-
required: true
|
|
821
|
-
},
|
|
822
|
-
/** FHIR Resource.id; logical id in URL. */
|
|
823
|
-
id: {
|
|
824
|
-
type: "string",
|
|
825
|
-
required: true
|
|
826
|
-
},
|
|
827
|
-
/** Full Workspace resource serialized as JSON string. */
|
|
828
|
-
resource: {
|
|
829
|
-
type: "string",
|
|
830
|
-
required: true
|
|
831
|
-
},
|
|
832
|
-
/**
|
|
833
|
-
* Summary projection (key display fields as JSON string: id, displayName, status).
|
|
834
|
-
* Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
|
|
835
|
-
*/
|
|
836
|
-
summary: {
|
|
837
|
-
type: "string",
|
|
838
|
-
required: true
|
|
839
|
-
},
|
|
840
|
-
/** Version id (e.g. ULID). */
|
|
841
|
-
vid: {
|
|
842
|
-
type: "string",
|
|
843
|
-
required: true
|
|
844
|
-
},
|
|
845
|
-
lastUpdated: {
|
|
846
|
-
type: "string",
|
|
847
|
-
required: true
|
|
848
|
-
},
|
|
849
|
-
gsi1Shard: gsi1ShardAttribute,
|
|
850
|
-
deleted: {
|
|
851
|
-
type: "boolean",
|
|
852
|
-
required: false
|
|
853
|
-
},
|
|
854
|
-
bundleId: {
|
|
855
|
-
type: "string",
|
|
856
|
-
required: false
|
|
857
|
-
},
|
|
858
|
-
msgId: {
|
|
859
|
-
type: "string",
|
|
860
|
-
required: false
|
|
861
|
-
}
|
|
862
|
-
},
|
|
863
|
-
indexes: {
|
|
864
|
-
/** Base table: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
|
|
865
|
-
record: {
|
|
866
|
-
pk: {
|
|
867
|
-
field: "PK",
|
|
868
|
-
composite: ["tenantId", "id"],
|
|
869
|
-
template: "TID#${tenantId}#WORKSPACE#ID#${id}"
|
|
870
|
-
},
|
|
871
|
-
sk: {
|
|
872
|
-
field: "SK",
|
|
873
|
-
composite: ["sk"],
|
|
874
|
-
template: "${sk}"
|
|
875
|
-
}
|
|
876
|
-
},
|
|
877
|
-
/**
|
|
878
|
-
* GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the
|
|
879
|
-
* four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.
|
|
880
|
-
* SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
|
|
881
|
-
* `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
|
|
882
|
-
*/
|
|
883
|
-
gsi1: {
|
|
884
|
-
index: "GSI1",
|
|
885
|
-
pk: {
|
|
886
|
-
field: "GSI1PK",
|
|
887
|
-
composite: ["tenantId", "gsi1Shard"],
|
|
888
|
-
template: "TID#${tenantId}#WID#-#RT#Workspace#SHARD#${gsi1Shard}"
|
|
889
|
-
},
|
|
890
|
-
sk: {
|
|
891
|
-
field: "GSI1SK",
|
|
892
|
-
casing: "none",
|
|
893
|
-
composite: ["lastUpdated", "id"],
|
|
894
|
-
template: "${lastUpdated}#${id}"
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
});
|
|
899
|
-
|
|
900
|
-
// src/data/dynamo/dynamo-control-service.ts
|
|
901
|
-
var controlPlaneEntities = {
|
|
902
|
-
configuration: ConfigurationEntity,
|
|
903
|
-
membership: MembershipEntity,
|
|
904
|
-
role: RoleEntity,
|
|
905
|
-
roleAssignment: RoleAssignmentEntity,
|
|
906
|
-
tenant: TenantEntity,
|
|
907
|
-
user: UserEntity,
|
|
908
|
-
workspace: WorkspaceEntity
|
|
909
|
-
};
|
|
910
|
-
var controlPlaneService = new Service(controlPlaneEntities, {
|
|
911
|
-
table: defaultTableName,
|
|
912
|
-
client: dynamoClient
|
|
913
|
-
});
|
|
914
|
-
var DynamoControlService = {
|
|
915
|
-
entities: controlPlaneService.entities
|
|
916
|
-
};
|
|
917
|
-
function getDynamoControlService(tableName) {
|
|
918
|
-
const resolved = tableName ?? defaultTableName;
|
|
919
|
-
const service = new Service(controlPlaneEntities, {
|
|
920
|
-
table: resolved,
|
|
921
|
-
client: dynamoClient
|
|
922
|
-
});
|
|
923
|
-
return {
|
|
924
|
-
entities: service.entities
|
|
925
|
-
};
|
|
926
|
-
}
|
|
927
|
-
|
|
928
134
|
// src/data/operations/control/configuration/configuration-create-operation.ts
|
|
929
135
|
var SK = "CURRENT";
|
|
930
136
|
async function createConfigurationOperation(params) {
|
|
@@ -1458,7 +664,7 @@ async function batchGetWithRetry(entity, keys) {
|
|
|
1458
664
|
while (pending.length > 0) {
|
|
1459
665
|
if (attempt > 0) {
|
|
1460
666
|
await new Promise(
|
|
1461
|
-
(
|
|
667
|
+
(resolve) => setTimeout(resolve, BATCH_GET_BASE_BACKOFF_MS * 2 ** (attempt - 1))
|
|
1462
668
|
);
|
|
1463
669
|
}
|
|
1464
670
|
attempt++;
|
|
@@ -3581,10 +2787,10 @@ import express8 from "express";
|
|
|
3581
2787
|
import { ulid } from "ulid";
|
|
3582
2788
|
|
|
3583
2789
|
// src/data/dynamo/dynamo-data-service.ts
|
|
3584
|
-
import { Service
|
|
2790
|
+
import { Service } from "electrodb";
|
|
3585
2791
|
|
|
3586
2792
|
// src/data/dynamo/entities/data-entity-common.ts
|
|
3587
|
-
import { Entity
|
|
2793
|
+
import { Entity } from "electrodb";
|
|
3588
2794
|
var dataEntityAttributes = {
|
|
3589
2795
|
/** Sort key. "CURRENT" for current version; version history in S3. */
|
|
3590
2796
|
sk: {
|
|
@@ -3680,7 +2886,7 @@ var dataEntityAttributes = {
|
|
|
3680
2886
|
}
|
|
3681
2887
|
};
|
|
3682
2888
|
function createDataEntity(entity, resourceTypeLabel) {
|
|
3683
|
-
return new
|
|
2889
|
+
return new Entity({
|
|
3684
2890
|
model: {
|
|
3685
2891
|
entity,
|
|
3686
2892
|
service: "data",
|
|
@@ -4594,7 +3800,7 @@ var dataPlaneEntities = {
|
|
|
4594
3800
|
visionprescription: VisionPrescriptionEntity,
|
|
4595
3801
|
verificationresult: VerificationResultEntity
|
|
4596
3802
|
};
|
|
4597
|
-
var dataPlaneService = new
|
|
3803
|
+
var dataPlaneService = new Service(dataPlaneEntities, {
|
|
4598
3804
|
table: defaultTableName,
|
|
4599
3805
|
client: dynamoClient
|
|
4600
3806
|
});
|
|
@@ -4603,7 +3809,7 @@ var DynamoDataService = {
|
|
|
4603
3809
|
};
|
|
4604
3810
|
function getDynamoDataService(tableName) {
|
|
4605
3811
|
const resolved = tableName ?? defaultTableName;
|
|
4606
|
-
const service = new
|
|
3812
|
+
const service = new Service(dataPlaneEntities, {
|
|
4607
3813
|
table: resolved,
|
|
4608
3814
|
client: dynamoClient
|
|
4609
3815
|
});
|
|
@@ -24905,133 +24111,8 @@ import express106 from "express";
|
|
|
24905
24111
|
|
|
24906
24112
|
// src/data/operations/data/patient/patient-create-operation.ts
|
|
24907
24113
|
import { ulid as ulid99 } from "ulid";
|
|
24908
|
-
|
|
24909
|
-
// src/data/import-patient.ts
|
|
24910
|
-
import { readFileSync } from "fs";
|
|
24911
|
-
import { resolve } from "path";
|
|
24912
|
-
import { extractSummary as extractSummary14 } from "@openhi/types";
|
|
24913
|
-
function extractPatient(parsed) {
|
|
24914
|
-
if (parsed && typeof parsed === "object" && "resourceType" in parsed) {
|
|
24915
|
-
const root = parsed;
|
|
24916
|
-
if (root.resourceType === "Patient" && root.id) {
|
|
24917
|
-
return root;
|
|
24918
|
-
}
|
|
24919
|
-
if (root.resourceType === "Bundle" && "entry" in parsed) {
|
|
24920
|
-
const entries = parsed.entry;
|
|
24921
|
-
if (Array.isArray(entries)) {
|
|
24922
|
-
const patientEntry = entries.find(
|
|
24923
|
-
(e) => e?.resource?.resourceType === "Patient" && e.resource.id
|
|
24924
|
-
);
|
|
24925
|
-
if (patientEntry?.resource) {
|
|
24926
|
-
return patientEntry.resource;
|
|
24927
|
-
}
|
|
24928
|
-
}
|
|
24929
|
-
}
|
|
24930
|
-
}
|
|
24931
|
-
throw new Error(
|
|
24932
|
-
"File must be a FHIR Patient resource or a Bundle containing at least one Patient entry"
|
|
24933
|
-
);
|
|
24934
|
-
}
|
|
24935
|
-
var SK12 = "CURRENT";
|
|
24936
|
-
var defaultAudit = {
|
|
24937
|
-
createdDate: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24938
|
-
createdById: "import",
|
|
24939
|
-
createdByName: "Bulk import",
|
|
24940
|
-
modifiedDate: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24941
|
-
modifiedById: "import",
|
|
24942
|
-
modifiedByName: "Bulk import"
|
|
24943
|
-
};
|
|
24944
|
-
function patientToPutAttrs(patient, options) {
|
|
24945
|
-
const {
|
|
24946
|
-
tenantId,
|
|
24947
|
-
workspaceId,
|
|
24948
|
-
createdDate,
|
|
24949
|
-
createdById,
|
|
24950
|
-
createdByName,
|
|
24951
|
-
modifiedDate,
|
|
24952
|
-
modifiedById,
|
|
24953
|
-
modifiedByName
|
|
24954
|
-
} = options;
|
|
24955
|
-
const lastUpdated = patient.meta?.lastUpdated ?? modifiedDate ?? defaultAudit.modifiedDate ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
24956
|
-
const auditMerged = {
|
|
24957
|
-
...defaultAudit,
|
|
24958
|
-
...createdDate != null && { createdDate },
|
|
24959
|
-
...createdById != null && { createdById },
|
|
24960
|
-
...createdByName != null && { createdByName },
|
|
24961
|
-
...modifiedDate != null && { modifiedDate },
|
|
24962
|
-
...modifiedById != null && { modifiedById },
|
|
24963
|
-
...modifiedByName != null && { modifiedByName }
|
|
24964
|
-
};
|
|
24965
|
-
const patientWithMeta = {
|
|
24966
|
-
...patient,
|
|
24967
|
-
meta: mergeAuditIntoMeta(patient.meta, auditMerged)
|
|
24968
|
-
};
|
|
24969
|
-
if (lastUpdated && !patientWithMeta.meta.lastUpdated) {
|
|
24970
|
-
patientWithMeta.meta.lastUpdated = lastUpdated;
|
|
24971
|
-
}
|
|
24972
|
-
return {
|
|
24973
|
-
sk: SK12,
|
|
24974
|
-
tenantId,
|
|
24975
|
-
workspaceId,
|
|
24976
|
-
id: patient.id,
|
|
24977
|
-
resource: compressResource(JSON.stringify(patientWithMeta)),
|
|
24978
|
-
summary: JSON.stringify(
|
|
24979
|
-
extractSummary14(patientWithMeta)
|
|
24980
|
-
),
|
|
24981
|
-
vid: lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36),
|
|
24982
|
-
lastUpdated,
|
|
24983
|
-
identifierSystem: "",
|
|
24984
|
-
identifierValue: "",
|
|
24985
|
-
facilityId: "",
|
|
24986
|
-
normalizedName: ""
|
|
24987
|
-
};
|
|
24988
|
-
}
|
|
24989
|
-
async function importPatientFromFile(filePath, options) {
|
|
24990
|
-
const resolved = resolve(filePath);
|
|
24991
|
-
const raw = readFileSync(resolved, "utf-8");
|
|
24992
|
-
const parsed = JSON.parse(raw);
|
|
24993
|
-
const patient = extractPatient(parsed);
|
|
24994
|
-
const service = getDynamoDataService(options.tableName);
|
|
24995
|
-
const attrs = patientToPutAttrs(patient, options);
|
|
24996
|
-
const result = await service.entities.patient.put(attrs).go();
|
|
24997
|
-
const data = result.data;
|
|
24998
|
-
if (!data) {
|
|
24999
|
-
throw new Error(`Put failed for Patient ${patient.id}`);
|
|
25000
|
-
}
|
|
25001
|
-
return {
|
|
25002
|
-
id: data.id,
|
|
25003
|
-
tenantId: data.tenantId,
|
|
25004
|
-
workspaceId: data.workspaceId
|
|
25005
|
-
};
|
|
25006
|
-
}
|
|
25007
|
-
async function main() {
|
|
25008
|
-
const [, , fileArg, tenantId = "tenant-1", workspaceId = "ws-1"] = process.argv;
|
|
25009
|
-
if (!fileArg) {
|
|
25010
|
-
console.error(
|
|
25011
|
-
"Usage: import-patient.ts <path-to-patient.json> [tenantId] [workspaceId]"
|
|
25012
|
-
);
|
|
25013
|
-
process.exit(1);
|
|
25014
|
-
}
|
|
25015
|
-
try {
|
|
25016
|
-
const result = await importPatientFromFile(fileArg, {
|
|
25017
|
-
tenantId,
|
|
25018
|
-
workspaceId
|
|
25019
|
-
});
|
|
25020
|
-
console.log(
|
|
25021
|
-
`Imported Patient ${result.id} (tenant=${result.tenantId}, workspace=${result.workspaceId})`
|
|
25022
|
-
);
|
|
25023
|
-
} catch (err) {
|
|
25024
|
-
console.error(err);
|
|
25025
|
-
process.exit(1);
|
|
25026
|
-
}
|
|
25027
|
-
}
|
|
25028
|
-
if (__require.main === module) {
|
|
25029
|
-
void main();
|
|
25030
|
-
}
|
|
25031
|
-
|
|
25032
|
-
// src/data/operations/data/patient/patient-create-operation.ts
|
|
25033
24114
|
async function createPatientOperation(params) {
|
|
25034
|
-
const { context, body } = params;
|
|
24115
|
+
const { context, body, tableName } = params;
|
|
25035
24116
|
const { tenantId, workspaceId, date, actorId, actorName } = context;
|
|
25036
24117
|
const id = body.id ?? ulid99();
|
|
25037
24118
|
const meta = {
|
|
@@ -25039,29 +24120,28 @@ async function createPatientOperation(params) {
|
|
|
25039
24120
|
lastUpdated: date,
|
|
25040
24121
|
versionId: "1"
|
|
25041
24122
|
};
|
|
25042
|
-
const
|
|
24123
|
+
const patientWithAudit = {
|
|
25043
24124
|
...body,
|
|
25044
24125
|
resourceType: "Patient",
|
|
25045
24126
|
id,
|
|
25046
|
-
meta
|
|
24127
|
+
meta: mergeAuditIntoMeta(meta, {
|
|
24128
|
+
createdDate: date,
|
|
24129
|
+
createdById: actorId,
|
|
24130
|
+
createdByName: actorName,
|
|
24131
|
+
modifiedDate: date,
|
|
24132
|
+
modifiedById: actorId,
|
|
24133
|
+
modifiedByName: actorName
|
|
24134
|
+
})
|
|
25047
24135
|
};
|
|
25048
|
-
const
|
|
24136
|
+
const service = getDynamoDataService(tableName);
|
|
24137
|
+
return createDataEntityRecord(
|
|
24138
|
+
service.entities.patient,
|
|
25049
24139
|
tenantId,
|
|
25050
24140
|
workspaceId,
|
|
25051
|
-
createdDate: date,
|
|
25052
|
-
createdById: actorId,
|
|
25053
|
-
createdByName: actorName,
|
|
25054
|
-
modifiedDate: date,
|
|
25055
|
-
modifiedById: actorId,
|
|
25056
|
-
modifiedByName: actorName
|
|
25057
|
-
};
|
|
25058
|
-
const service = getDynamoDataService(params.tableName);
|
|
25059
|
-
const attrs = patientToPutAttrs(patient, options);
|
|
25060
|
-
await service.entities.patient.put(attrs).go();
|
|
25061
|
-
return {
|
|
25062
24141
|
id,
|
|
25063
|
-
|
|
25064
|
-
|
|
24142
|
+
patientWithAudit,
|
|
24143
|
+
date
|
|
24144
|
+
);
|
|
25065
24145
|
}
|
|
25066
24146
|
|
|
25067
24147
|
// src/data/rest-api/routes/data/patient/patient-create-route.ts
|