@openhi/constructs 0.0.32 → 0.0.33

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.
@@ -37,7 +37,7 @@ var import_serverless_express2 = __toESM(require("@codegenie/serverless-express"
37
37
 
38
38
  // src/data/rest-api/rest-api.ts
39
39
  var import_node_path2 = __toESM(require("path"));
40
- var import_express3 = __toESM(require("express"));
40
+ var import_express5 = __toESM(require("express"));
41
41
 
42
42
  // src/data/middleware/normalize-json-body.ts
43
43
  function normalizeJsonBodyMiddleware(req, _res, next) {
@@ -102,13 +102,9 @@ function openHiContextMiddleware(req, res, next) {
102
102
  next();
103
103
  }
104
104
 
105
- // src/data/rest-api/routes/configuration/configuration.ts
105
+ // src/data/rest-api/routes/control/configuration/configuration.ts
106
106
  var import_express = __toESM(require("express"));
107
107
 
108
- // src/data/rest-api/routes/configuration/configuration-common.ts
109
- var BASE_PATH = "/Configuration";
110
- var SK = "CURRENT";
111
-
112
108
  // src/lib/compression.ts
113
109
  var import_node_zlib = require("zlib");
114
110
  var ENVELOPE_VERSION = 1;
@@ -165,16 +161,26 @@ function decompressResource(compressedOrRaw) {
165
161
  return compressedOrRaw;
166
162
  }
167
163
 
168
- // src/data/dynamo/dynamo-service.ts
164
+ // src/data/dynamo/dynamo-control-service.ts
165
+ var import_electrodb2 = require("electrodb");
166
+
167
+ // src/data/dynamo/dynamo-client.ts
169
168
  var import_client_dynamodb = require("@aws-sdk/client-dynamodb");
170
- var import_electrodb3 = require("electrodb");
169
+ var defaultTableName = process.env.DYNAMO_TABLE_NAME ?? "jesttesttable";
170
+ var dynamoClient = new import_client_dynamodb.DynamoDBClient({
171
+ ...process.env.MOCK_DYNAMODB_ENDPOINT && {
172
+ endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
173
+ sslEnabled: false,
174
+ region: "local"
175
+ }
176
+ });
171
177
 
172
- // src/data/dynamo/entities/configuration.ts
178
+ // src/data/dynamo/entities/control/configuration-entity.ts
173
179
  var import_electrodb = require("electrodb");
174
- var Configuration = new import_electrodb.Entity({
180
+ var ConfigurationEntity = new import_electrodb.Entity({
175
181
  model: {
176
182
  entity: "configuration",
177
- service: "openhi",
183
+ service: "control",
178
184
  version: "01"
179
185
  },
180
186
  attributes: {
@@ -251,7 +257,7 @@ var Configuration = new import_electrodb.Entity({
251
257
  pk: {
252
258
  field: "PK",
253
259
  composite: ["tenantId", "workspaceId", "userId", "roleId"],
254
- template: "OHI#CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}"
260
+ template: "CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}"
255
261
  },
256
262
  sk: {
257
263
  field: "SK",
@@ -277,213 +283,134 @@ var Configuration = new import_electrodb.Entity({
277
283
  }
278
284
  });
279
285
 
280
- // src/data/dynamo/entities/patient.ts
281
- var import_electrodb2 = require("electrodb");
282
- var Patient = new import_electrodb2.Entity({
283
- model: {
284
- entity: "patient",
285
- service: "openhi",
286
- version: "01"
287
- },
288
- attributes: {
289
- /** Sort key. "CURRENT" for current version; version history in S3. */
290
- sk: {
291
- type: "string",
292
- required: true,
293
- default: "CURRENT"
294
- },
295
- tenantId: {
296
- type: "string",
297
- required: true
298
- },
299
- workspaceId: {
300
- type: "string",
301
- required: true
302
- },
303
- /** FHIR Resource.id; logical id in URL and PK. */
304
- id: {
305
- type: "string",
306
- required: true
307
- },
308
- /** FHIR resource as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */
309
- resource: {
310
- type: "string",
311
- required: true
312
- },
313
- /** Version id (e.g. ULID). Tracks current version; S3 history key. */
314
- vid: {
315
- type: "string",
316
- required: true
317
- },
318
- lastUpdated: {
319
- type: "string",
320
- required: true
321
- },
322
- deleted: {
323
- type: "boolean",
324
- required: false
325
- },
326
- bundleId: {
327
- type: "string",
328
- required: false
329
- },
330
- msgId: {
331
- type: "string",
332
- required: false
333
- },
334
- // Audit is in FHIR resource meta (meta.extension), not item attributes. See data-store-entities.md.
335
- // --- GSI2 (Identifier Lookup): optional; set when indexing this patient by identifier (e.g. MRN)
336
- /** Identifier system (e.g. MRN system URI). When set with identifierValue, item is written to GSI2. */
337
- identifierSystem: {
338
- type: "string",
339
- required: false
340
- },
341
- /** Identifier value (e.g. MRN). When set with identifierSystem, item is written to GSI2. */
342
- identifierValue: {
343
- type: "string",
344
- required: false
345
- },
346
- /** For GSI2/GSI3 projection: base table PK/SK so GetItem can be used after query. */
347
- resourcePk: {
348
- type: "string",
349
- required: false
350
- },
351
- resourceSk: {
352
- type: "string",
353
- required: false
354
- },
355
- /** For GSI2 projection: display name for roster/lookup. */
356
- display: {
357
- type: "string",
358
- required: false
359
- },
360
- /** For GSI2 projection: resource status if applicable. */
361
- status: {
362
- type: "string",
363
- required: false
364
- },
365
- // --- GSI3 (Facility Ops): optional; set when indexing this patient on a facility roster
366
- /** Facility id. When set with normalizedName, item is written to GSI3. */
367
- facilityId: {
368
- type: "string",
369
- required: false
370
- },
371
- /** Normalized display name for roster sort. When set with facilityId, item is written to GSI3. */
372
- normalizedName: {
373
- type: "string",
374
- required: false
375
- }
376
- },
377
- indexes: {
378
- /** Base table: PK, SK (data store key names). PK is built from tenantId, workspaceId, id; do not supply PK from outside. */
379
- record: {
380
- pk: {
381
- field: "PK",
382
- composite: ["tenantId", "workspaceId", "id"],
383
- template: "TID#${tenantId}#WID#${workspaceId}#RT#Patient#ID#${id}"
384
- },
385
- sk: {
386
- field: "SK",
387
- composite: ["sk"]
388
- }
389
- },
390
- /**
391
- * GSI1 — Reverse Reference: query "what references this patient?".
392
- * Patient items are never written to GSI1 (condition: false); reference index items
393
- * are written by other resources. This index enables querying GSI1 by REFTO#RT#Patient#ID#<id>.
394
- */
395
- gsi1: {
396
- index: "GSI1",
397
- condition: () => false,
398
- pk: {
399
- field: "GSI1PK",
400
- composite: ["tenantId", "workspaceId", "id"],
401
- template: "TID#${tenantId}#WID#${workspaceId}#REFTO#RT#Patient#ID#${id}"
402
- },
403
- sk: {
404
- field: "GSI1SK",
405
- composite: []
406
- }
407
- },
408
- /** GSI2 — Identifier Lookup: MRN, NPI, member ID, etc. Keys built from identifier components. */
409
- gsi2: {
410
- index: "GSI2",
411
- condition: (attr) => attr.identifierSystem != null && attr.identifierValue != null,
412
- pk: {
413
- field: "GSI2PK",
414
- composite: [
415
- "tenantId",
416
- "workspaceId",
417
- "identifierSystem",
418
- "identifierValue"
419
- ],
420
- template: "TID#${tenantId}#WID#${workspaceId}#IDENT#${identifierSystem}#${identifierValue}"
421
- },
422
- sk: {
423
- field: "GSI2SK",
424
- composite: ["id"],
425
- template: "RT#Patient#ID#${id}"
426
- }
427
- },
428
- /** GSI3 — Facility Ops: facility roster, worklists. Keys built from facility + name. */
429
- gsi3: {
430
- index: "GSI3",
431
- condition: (attr) => attr.facilityId != null && attr.normalizedName != null,
432
- pk: {
433
- field: "GSI3PK",
434
- composite: ["tenantId", "workspaceId", "facilityId"],
435
- template: "TID#${tenantId}#WID#${workspaceId}#FAC#${facilityId}"
436
- },
437
- sk: {
438
- field: "GSI3SK",
439
- composite: ["id", "normalizedName"],
440
- template: "TYPE#PATIENT#PAT#${id}#NAME#${normalizedName}"
441
- }
442
- },
443
- /** GSI4 — Resource Type Index: list all Patients in workspace (no scan). */
444
- gsi4: {
445
- index: "GSI4",
446
- condition: () => true,
447
- pk: {
448
- field: "GSI4PK",
449
- composite: ["tenantId", "workspaceId"],
450
- template: "TID#${tenantId}#WID#${workspaceId}#RT#Patient"
451
- },
452
- sk: {
453
- field: "GSI4SK",
454
- composite: ["id"],
455
- template: "ID#${id}"
456
- }
457
- }
458
- }
459
- });
460
-
461
- // src/data/dynamo/dynamo-service.ts
462
- var table = process.env.DYNAMO_TABLE_NAME ?? "jesttesttable";
463
- var client = new import_client_dynamodb.DynamoDBClient({
464
- ...process.env.MOCK_DYNAMODB_ENDPOINT && {
465
- endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
466
- sslEnabled: false,
467
- region: "local"
468
- }
286
+ // src/data/dynamo/dynamo-control-service.ts
287
+ var controlPlaneEntities = {
288
+ configuration: ConfigurationEntity
289
+ };
290
+ var controlPlaneService = new import_electrodb2.Service(controlPlaneEntities, {
291
+ table: defaultTableName,
292
+ client: dynamoClient
469
293
  });
470
- var entities = { patient: Patient, configuration: Configuration };
471
- var DynamoDataService = new import_electrodb3.Service(entities, { table, client });
472
- function getDynamoDataService(tableName) {
473
- const resolved = tableName ?? table;
474
- return new import_electrodb3.Service(entities, { table: resolved, client });
294
+ var DynamoControlService = {
295
+ entities: controlPlaneService.entities
296
+ };
297
+ function getDynamoControlService(tableName) {
298
+ const resolved = tableName ?? defaultTableName;
299
+ const service = new import_electrodb2.Service(controlPlaneEntities, {
300
+ table: resolved,
301
+ client: dynamoClient
302
+ });
303
+ return {
304
+ entities: service.entities
305
+ };
475
306
  }
476
307
 
477
- // src/data/rest-api/routes/configuration/configuration-create.ts
478
- async function createConfiguration(req, res) {
479
- const ctx = req.openhiContext;
308
+ // src/data/operations/control/configuration/configuration-create-operation.ts
309
+ var SK = "CURRENT";
310
+ async function createConfigurationOperation(params) {
311
+ const { context, body, tableName } = params;
480
312
  const {
481
313
  tenantId: ctxTenantId,
482
314
  workspaceId: ctxWorkspaceId,
483
315
  actorId: ctxActorId,
484
316
  date
485
- } = ctx;
486
- const body = req.body;
317
+ } = context;
318
+ const key = body.key;
319
+ const id = body.id ?? `config-${key}-${Date.now()}`;
320
+ const resourcePayload = body.resource;
321
+ const resourceStr = typeof resourcePayload === "string" ? resourcePayload : JSON.stringify(resourcePayload ?? {});
322
+ const tenantId = body.tenantId ?? ctxTenantId;
323
+ const workspaceId = body.workspaceId ?? ctxWorkspaceId;
324
+ const userId = body.userId ?? ctxActorId ?? "-";
325
+ const roleId = body.roleId ?? context.roleId ?? "-";
326
+ const lastUpdated = body.lastUpdated ?? date;
327
+ const vid = body.vid ?? (date.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36));
328
+ const service = getDynamoControlService(tableName);
329
+ await service.entities.configuration.put({
330
+ tenantId,
331
+ workspaceId,
332
+ userId,
333
+ roleId,
334
+ key,
335
+ id,
336
+ resource: compressResource(resourceStr),
337
+ vid,
338
+ lastUpdated,
339
+ sk: SK
340
+ }).go();
341
+ const resource = typeof resourcePayload === "object" ? resourcePayload : JSON.parse(resourceStr);
342
+ return {
343
+ id,
344
+ key,
345
+ resource: {
346
+ resourceType: "Configuration",
347
+ id,
348
+ key,
349
+ resource
350
+ },
351
+ meta: { lastUpdated, versionId: vid }
352
+ };
353
+ }
354
+
355
+ // src/data/rest-api/routes/common.ts
356
+ var BASE_PATH = {
357
+ CONFIGURATION: "/Configuration",
358
+ ENCOUNTER: "/Encounter",
359
+ PATIENT: "/Patient",
360
+ PRACTITIONER: "/Practitioner"
361
+ };
362
+ function requireJsonBody(req, res) {
363
+ const raw = req.body;
364
+ if (raw == null || typeof raw !== "object" || Array.isArray(raw)) {
365
+ return {
366
+ errorResponse: res.status(400).json({
367
+ resourceType: "OperationOutcome",
368
+ issue: [
369
+ {
370
+ severity: "error",
371
+ code: "invalid",
372
+ diagnostics: "Request body must be a JSON object."
373
+ }
374
+ ]
375
+ })
376
+ };
377
+ }
378
+ return { body: raw };
379
+ }
380
+ function buildSearchsetBundle(basePath, entries) {
381
+ return {
382
+ resourceType: "Bundle",
383
+ type: "searchset",
384
+ total: entries.length,
385
+ link: [{ relation: "self", url: basePath }],
386
+ entry: entries.map((item) => ({
387
+ fullUrl: `${basePath}/${item.id}`,
388
+ resource: item.resource
389
+ }))
390
+ };
391
+ }
392
+ function sendOperationOutcome404(res, diagnostics) {
393
+ return res.status(404).json({
394
+ resourceType: "OperationOutcome",
395
+ issue: [{ severity: "error", code: "not-found", diagnostics }]
396
+ });
397
+ }
398
+ function sendOperationOutcome500(res, err, logContext) {
399
+ if (logContext) {
400
+ console.error(logContext, err);
401
+ }
402
+ return res.status(500).json({
403
+ resourceType: "OperationOutcome",
404
+ issue: [{ severity: "error", code: "exception", diagnostics: String(err) }]
405
+ });
406
+ }
407
+
408
+ // src/data/rest-api/routes/control/configuration/configuration-create-route.ts
409
+ async function createConfigurationRoute(req, res) {
410
+ const bodyResult = requireJsonBody(req, res);
411
+ if ("errorResponse" in bodyResult) return bodyResult.errorResponse;
412
+ const ctx = req.openhiContext;
413
+ const body = bodyResult.body;
487
414
  const key = body?.key;
488
415
  if (!key || typeof key !== "string") {
489
416
  return res.status(400).json({
@@ -497,37 +424,26 @@ async function createConfiguration(req, res) {
497
424
  ]
498
425
  });
499
426
  }
500
- const id = body?.id ?? `config-${key}-${Date.now()}`;
501
- const resourcePayload = body?.resource;
502
- const resourceStr = typeof resourcePayload === "string" ? resourcePayload : JSON.stringify(resourcePayload ?? {});
503
- const tenantId = body?.tenantId ?? ctxTenantId;
504
- const workspaceId = body?.workspaceId ?? ctxWorkspaceId;
505
- const userId = body?.userId ?? ctxActorId ?? "-";
506
- const roleId = body?.roleId ?? "-";
507
- const vid = body?.vid ?? (date.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36));
508
- const lastUpdated = body?.lastUpdated ?? date;
509
- const service = getDynamoDataService();
510
427
  try {
511
- await service.entities.configuration.put({
512
- tenantId,
513
- workspaceId,
514
- userId,
515
- roleId,
516
- key,
517
- id,
518
- resource: compressResource(resourceStr),
519
- vid,
520
- lastUpdated,
521
- sk: SK
522
- }).go();
428
+ const result = await createConfigurationOperation({
429
+ context: ctx,
430
+ body: {
431
+ key,
432
+ id: body?.id,
433
+ resource: body?.resource,
434
+ tenantId: body?.tenantId,
435
+ workspaceId: body?.workspaceId,
436
+ userId: body?.userId,
437
+ roleId: body?.roleId,
438
+ vid: body?.vid,
439
+ lastUpdated: body?.lastUpdated
440
+ }
441
+ });
523
442
  const config = {
524
- resourceType: "Configuration",
525
- id,
526
- key,
527
- resource: typeof resourcePayload === "object" ? resourcePayload : JSON.parse(resourceStr),
528
- meta: { lastUpdated, versionId: vid }
443
+ ...result.resource,
444
+ meta: result.meta
529
445
  };
530
- return res.status(201).location(`${BASE_PATH}/${key}`).json(config);
446
+ return res.status(201).location(`${BASE_PATH.CONFIGURATION}/${result.key}`).json(config);
531
447
  } catch (err) {
532
448
  console.error("POST Configuration error:", err);
533
449
  return res.status(500).json({
@@ -539,15 +455,29 @@ async function createConfiguration(req, res) {
539
455
  }
540
456
  }
541
457
 
542
- // src/data/rest-api/routes/configuration/configuration-delete.ts
543
- async function deleteConfiguration(req, res) {
544
- const key = String(req.params.key);
545
- const ctx = req.openhiContext;
546
- const { tenantId, workspaceId, actorId, roleId: ctxRoleId } = ctx;
458
+ // src/data/operations/control/configuration/configuration-delete-operation.ts
459
+ var SK2 = "CURRENT";
460
+ async function deleteConfigurationOperation(params) {
461
+ const { context, id, tableName } = params;
462
+ const { tenantId, workspaceId, actorId, roleId: ctxRoleId } = context;
547
463
  const roleId = ctxRoleId ?? "-";
548
- const service = getDynamoDataService();
464
+ const service = getDynamoControlService(tableName);
465
+ await service.entities.configuration.delete({
466
+ tenantId,
467
+ workspaceId,
468
+ userId: actorId,
469
+ roleId,
470
+ key: id,
471
+ sk: SK2
472
+ }).go();
473
+ }
474
+
475
+ // src/data/rest-api/routes/control/configuration/configuration-delete-route.ts
476
+ async function deleteConfigurationRoute(req, res) {
477
+ const id = String(req.params.id);
478
+ const ctx = req.openhiContext;
549
479
  try {
550
- await service.entities.configuration.delete({ tenantId, workspaceId, userId: actorId, roleId, key, sk: SK }).go();
480
+ await deleteConfigurationOperation({ context: ctx, id });
551
481
  return res.status(204).send();
552
482
  } catch (err) {
553
483
  console.error("DELETE Configuration error:", err);
@@ -560,37 +490,87 @@ async function deleteConfiguration(req, res) {
560
490
  }
561
491
  }
562
492
 
563
- // src/data/rest-api/routes/configuration/configuration-get-by-key.ts
564
- async function getConfigurationByKey(req, res) {
565
- const key = String(req.params.key);
566
- const ctx = req.openhiContext;
567
- const { tenantId, workspaceId, actorId, roleId: ctxRoleId } = ctx;
493
+ // src/data/errors/domain-errors.ts
494
+ var DomainError = class extends Error {
495
+ constructor(message, code, options) {
496
+ super(message, options);
497
+ this.name = this.constructor.name;
498
+ this.code = code;
499
+ this.details = options?.details;
500
+ Object.setPrototypeOf(this, new.target.prototype);
501
+ }
502
+ };
503
+ var NotFoundError = class extends DomainError {
504
+ constructor(message, options) {
505
+ super(message, "NOT_FOUND", options);
506
+ }
507
+ };
508
+ var ValidationError = class extends DomainError {
509
+ constructor(message, options) {
510
+ super(message, "VALIDATION", options);
511
+ }
512
+ };
513
+ var ConflictError = class extends DomainError {
514
+ constructor(message, options) {
515
+ super(message, "CONFLICT", options);
516
+ }
517
+ };
518
+ function domainErrorToHttpStatus(err) {
519
+ if (err instanceof NotFoundError) return 404;
520
+ if (err instanceof ValidationError) return 400;
521
+ if (err instanceof ConflictError) return 409;
522
+ return null;
523
+ }
524
+
525
+ // src/data/operations/control/configuration/configuration-get-by-id-operation.ts
526
+ var SK3 = "CURRENT";
527
+ async function getConfigurationByIdOperation(params) {
528
+ const { context, id, tableName } = params;
529
+ const { tenantId, workspaceId, actorId, roleId: ctxRoleId } = context;
568
530
  const roleId = ctxRoleId ?? "-";
569
- const service = getDynamoDataService();
531
+ const service = getDynamoControlService(tableName);
532
+ const result = await service.entities.configuration.get({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK3 }).go();
533
+ if (!result.data) {
534
+ throw new NotFoundError(`Configuration ${id} not found`, {
535
+ details: { id }
536
+ });
537
+ }
538
+ const resource = JSON.parse(
539
+ decompressResource(result.data.resource)
540
+ );
541
+ return {
542
+ id: result.data.id,
543
+ key: result.data.key,
544
+ resource: {
545
+ ...resource,
546
+ resourceType: "Configuration",
547
+ id: result.data.id,
548
+ key: result.data.key
549
+ }
550
+ };
551
+ }
552
+
553
+ // src/data/rest-api/routes/control/configuration/configuration-get-by-id-route.ts
554
+ async function getConfigurationByIdRoute(req, res) {
555
+ const id = String(req.params.id);
556
+ const ctx = req.openhiContext;
570
557
  try {
571
- const result = await service.entities.configuration.get({ tenantId, workspaceId, userId: actorId, roleId, key, sk: SK }).go();
572
- if (!result.data) {
558
+ const result = await getConfigurationByIdOperation({ context: ctx, id });
559
+ return res.json(result.resource);
560
+ } catch (err) {
561
+ const status = domainErrorToHttpStatus(err);
562
+ if (status === 404) {
573
563
  return res.status(404).json({
574
564
  resourceType: "OperationOutcome",
575
565
  issue: [
576
566
  {
577
567
  severity: "error",
578
568
  code: "not-found",
579
- diagnostics: `Configuration ${key} not found`
569
+ diagnostics: err instanceof NotFoundError ? err.message : `Configuration ${id} not found`
580
570
  }
581
571
  ]
582
572
  });
583
573
  }
584
- const resource = JSON.parse(
585
- decompressResource(result.data.resource)
586
- );
587
- return res.json({
588
- ...resource,
589
- resourceType: "Configuration",
590
- id: result.data.id,
591
- key: result.data.key
592
- });
593
- } catch (err) {
594
574
  console.error("GET Configuration error:", err);
595
575
  return res.status(500).json({
596
576
  resourceType: "OperationOutcome",
@@ -605,6 +585,30 @@ async function getConfigurationByKey(req, res) {
605
585
  }
606
586
  }
607
587
 
588
+ // src/data/operations/control/configuration/configuration-list-operation.ts
589
+ async function listConfigurationsOperation(params) {
590
+ const { context, tableName } = params;
591
+ const { tenantId, workspaceId } = context;
592
+ const service = getDynamoControlService(tableName);
593
+ const result = await service.entities.configuration.query.gsi4({ tenantId, workspaceId }).go();
594
+ const entries = (result.data ?? []).map(
595
+ (item) => {
596
+ const resource = JSON.parse(decompressResource(item.resource));
597
+ return {
598
+ id: item.id,
599
+ key: item.key,
600
+ resource: {
601
+ resourceType: "Configuration",
602
+ id: item.id,
603
+ key: item.key,
604
+ resource
605
+ }
606
+ };
607
+ }
608
+ );
609
+ return { entries };
610
+ }
611
+
608
612
  // src/data/rest-api/dynamic-configuration.ts
609
613
  var import_client_ssm = require("@aws-sdk/client-ssm");
610
614
  var BASE_PATH2 = "/Configuration";
@@ -624,9 +628,9 @@ async function getDynamicConfigurationEntries(context) {
624
628
  return getStaticDummyEntry(context);
625
629
  }
626
630
  const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION;
627
- const client2 = new import_client_ssm.SSMClient({ region });
631
+ const client = new import_client_ssm.SSMClient({ region });
628
632
  try {
629
- const describeResult = await client2.send(
633
+ const describeResult = await client.send(
630
634
  new import_client_ssm.DescribeParametersCommand({
631
635
  ParameterFilters: [
632
636
  {
@@ -650,7 +654,7 @@ async function getDynamicConfigurationEntries(context) {
650
654
  const parameters = [];
651
655
  for (let i = 0; i < names.length; i += 10) {
652
656
  const batch = names.slice(i, i + 10);
653
- const getResult = await client2.send(
657
+ const getResult = await client.send(
654
658
  new import_client_ssm.GetParametersCommand({
655
659
  Names: batch,
656
660
  WithDecryption: true
@@ -714,29 +718,28 @@ function getStaticDummyEntry(_context) {
714
718
  return [dummy];
715
719
  }
716
720
 
717
- // src/data/rest-api/routes/configuration/configuration-list.ts
718
- async function listConfigurations(req, res) {
719
- const { tenantId, workspaceId } = req.openhiContext;
720
- const service = getDynamoDataService();
721
+ // src/data/rest-api/routes/control/configuration/configuration-list-route.ts
722
+ async function listConfigurationsRoute(req, res) {
723
+ const ctx = req.openhiContext;
724
+ const { tenantId, workspaceId } = ctx;
721
725
  try {
722
- const result = await service.entities.configuration.query.gsi4({ tenantId, workspaceId }).go();
723
- const dynamoEntries = (result.data ?? []).map((item) => {
724
- const resource = JSON.parse(decompressResource(item.resource));
725
- return {
726
- fullUrl: `${BASE_PATH}/${item.key}`,
727
- resource: { ...resource, id: item.id, key: item.key }
728
- };
726
+ const { entries: dynamoEntries } = await listConfigurationsOperation({
727
+ context: ctx
729
728
  });
729
+ const dynamoBundleEntries = dynamoEntries.map((e) => ({
730
+ fullUrl: `${BASE_PATH.CONFIGURATION}/${e.key}`,
731
+ resource: e.resource
732
+ }));
730
733
  const dynamicEntries = await getDynamicConfigurationEntries({
731
734
  tenantId,
732
735
  workspaceId
733
736
  });
734
- const entries = [...dynamoEntries, ...dynamicEntries];
737
+ const entries = [...dynamoBundleEntries, ...dynamicEntries];
735
738
  const bundle = {
736
739
  resourceType: "Bundle",
737
740
  type: "searchset",
738
741
  total: entries.length,
739
- link: [{ relation: "self", url: BASE_PATH }],
742
+ link: [{ relation: "self", url: BASE_PATH.CONFIGURATION }],
740
743
  entry: entries
741
744
  };
742
745
  return res.json(bundle);
@@ -755,114 +758,1180 @@ async function listConfigurations(req, res) {
755
758
  }
756
759
  }
757
760
 
758
- // src/data/rest-api/routes/configuration/configuration-update.ts
759
- async function updateConfiguration(req, res) {
760
- const key = String(req.params.key);
761
- const ctx = req.openhiContext;
762
- const { tenantId, workspaceId, actorId, date, roleId: ctxRoleId } = ctx;
761
+ // src/data/operations/control/configuration/configuration-update-operation.ts
762
+ var SK4 = "CURRENT";
763
+ async function updateConfigurationOperation(params) {
764
+ const { context, id, body, tableName } = params;
765
+ const { tenantId, workspaceId, actorId, date, roleId: ctxRoleId } = context;
763
766
  const roleId = ctxRoleId ?? "-";
764
- const body = req.body;
765
- const resourcePayload = body?.resource;
767
+ const service = getDynamoControlService(tableName);
768
+ const existing = await service.entities.configuration.get({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK4 }).go();
769
+ if (!existing.data) {
770
+ throw new NotFoundError(`Configuration ${id} not found`, {
771
+ details: { id }
772
+ });
773
+ }
774
+ const resourcePayload = body.resource;
766
775
  const resourceStr = typeof resourcePayload === "string" ? resourcePayload : JSON.stringify(resourcePayload ?? {});
767
- const lastUpdated = body?.lastUpdated ?? date;
768
- const service = getDynamoDataService();
769
- try {
770
- const existing = await service.entities.configuration.get({ tenantId, workspaceId, userId: actorId, roleId, key, sk: SK }).go();
771
- if (!existing.data) {
772
- return res.status(404).json({
773
- resourceType: "OperationOutcome",
774
- issue: [
775
- {
776
- severity: "error",
776
+ const lastUpdated = body.lastUpdated ?? date;
777
+ const nextVid = existing.data.vid != null ? String(Number(existing.data.vid) + 1) : date.replace(/[-:T.Z]/g, "").slice(0, 12) || "2";
778
+ await service.entities.configuration.patch({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK4 }).set({
779
+ resource: compressResource(resourceStr),
780
+ lastUpdated,
781
+ vid: nextVid
782
+ }).go();
783
+ const parsedResource = typeof resourcePayload === "object" ? resourcePayload : JSON.parse(resourceStr);
784
+ return {
785
+ id: existing.data.id,
786
+ key: existing.data.key,
787
+ resource: {
788
+ resourceType: "Configuration",
789
+ id: existing.data.id,
790
+ key: existing.data.key,
791
+ resource: parsedResource
792
+ },
793
+ meta: { lastUpdated, versionId: nextVid }
794
+ };
795
+ }
796
+
797
+ // src/data/rest-api/routes/control/configuration/configuration-update-route.ts
798
+ async function updateConfigurationRoute(req, res) {
799
+ const bodyResult = requireJsonBody(req, res);
800
+ if ("errorResponse" in bodyResult) return bodyResult.errorResponse;
801
+ const id = String(req.params.id);
802
+ const ctx = req.openhiContext;
803
+ const body = bodyResult.body;
804
+ try {
805
+ const result = await updateConfigurationOperation({
806
+ context: ctx,
807
+ id,
808
+ body: {
809
+ resource: body?.resource,
810
+ lastUpdated: body?.lastUpdated
811
+ }
812
+ });
813
+ return res.json({
814
+ ...result.resource,
815
+ meta: result.meta
816
+ });
817
+ } catch (err) {
818
+ const status = domainErrorToHttpStatus(err);
819
+ if (status === 404) {
820
+ return res.status(404).json({
821
+ resourceType: "OperationOutcome",
822
+ issue: [
823
+ {
824
+ severity: "error",
777
825
  code: "not-found",
778
- diagnostics: `Configuration ${key} not found`
826
+ diagnostics: err instanceof NotFoundError ? err.message : `Configuration ${id} not found`
779
827
  }
780
828
  ]
781
829
  });
782
830
  }
783
- const nextVid = existing.data.vid != null ? String(Number(existing.data.vid) + 1) : date.replace(/[-:T.Z]/g, "").slice(0, 12) || "2";
784
- await service.entities.configuration.patch({ tenantId, workspaceId, userId: actorId, roleId, key, sk: SK }).set({
785
- resource: compressResource(resourceStr),
786
- lastUpdated,
787
- vid: nextVid
788
- }).go();
789
- const config = {
790
- resourceType: "Configuration",
791
- id: existing.data.id,
792
- key: existing.data.key,
793
- resource: typeof resourcePayload === "object" ? resourcePayload : JSON.parse(resourceStr),
794
- meta: { lastUpdated, versionId: nextVid }
795
- };
796
- return res.json(config);
797
- } catch (err) {
798
831
  console.error("PUT Configuration error:", err);
799
832
  return res.status(500).json({
800
833
  resourceType: "OperationOutcome",
801
834
  issue: [
802
- { severity: "error", code: "exception", diagnostics: String(err) }
835
+ {
836
+ severity: "error",
837
+ code: "exception",
838
+ diagnostics: String(err)
839
+ }
803
840
  ]
804
841
  });
805
842
  }
806
843
  }
807
844
 
808
- // src/data/rest-api/routes/configuration/configuration.ts
845
+ // src/data/rest-api/routes/control/configuration/configuration.ts
809
846
  var router = import_express.default.Router();
810
- router.get("/", listConfigurations);
811
- router.get("/:key", getConfigurationByKey);
812
- router.post("/", createConfiguration);
813
- router.put("/:key", updateConfiguration);
814
- router.delete("/:key", deleteConfiguration);
847
+ router.get("/", listConfigurationsRoute);
848
+ router.get("/:id", getConfigurationByIdRoute);
849
+ router.post("/", createConfigurationRoute);
850
+ router.put("/:id", updateConfigurationRoute);
851
+ router.delete("/:id", deleteConfigurationRoute);
815
852
 
816
- // src/data/rest-api/routes/patient/patient.ts
853
+ // src/data/rest-api/routes/data/encounter/encounter.ts
817
854
  var import_express2 = __toESM(require("express"));
818
855
 
819
- // src/data/rest-api/routes/patient/patient-common.ts
820
- var BASE_PATH3 = "/Patient";
821
- var SK2 = "CURRENT";
822
- function requireJsonBody(req, res) {
823
- const raw = req.body;
824
- if (raw == null || typeof raw !== "object" || Array.isArray(raw)) {
856
+ // src/data/operations/data/encounter/encounter-create-operation.ts
857
+ var import_ulid = require("ulid");
858
+
859
+ // src/data/audit-meta.ts
860
+ var OPENHI_EXT = "http://openhi.org/fhir/StructureDefinition";
861
+ function mergeAuditIntoMeta(meta, audit) {
862
+ const existing = meta ?? {};
863
+ const ext = [
864
+ ...Array.isArray(existing.extension) ? existing.extension : []
865
+ ];
866
+ const byUrl = new Map(ext.map((e) => [e.url, e]));
867
+ function set(url, value, type) {
868
+ if (value == null) return;
869
+ byUrl.set(url, { url, [type]: value });
870
+ }
871
+ set(`${OPENHI_EXT}/created-date`, audit.createdDate, "valueDateTime");
872
+ set(`${OPENHI_EXT}/created-by-id`, audit.createdById, "valueString");
873
+ set(`${OPENHI_EXT}/created-by-name`, audit.createdByName, "valueString");
874
+ set(`${OPENHI_EXT}/modified-date`, audit.modifiedDate, "valueDateTime");
875
+ set(`${OPENHI_EXT}/modified-by-id`, audit.modifiedById, "valueString");
876
+ set(`${OPENHI_EXT}/modified-by-name`, audit.modifiedByName, "valueString");
877
+ set(`${OPENHI_EXT}/deleted-date`, audit.deletedDate, "valueDateTime");
878
+ set(`${OPENHI_EXT}/deleted-by-id`, audit.deletedById, "valueString");
879
+ set(`${OPENHI_EXT}/deleted-by-name`, audit.deletedByName, "valueString");
880
+ return { ...existing, extension: Array.from(byUrl.values()) };
881
+ }
882
+
883
+ // src/data/dynamo/dynamo-data-service.ts
884
+ var import_electrodb4 = require("electrodb");
885
+
886
+ // src/data/dynamo/entities/data-entity-common.ts
887
+ var import_electrodb3 = require("electrodb");
888
+ var dataEntityAttributes = {
889
+ /** Sort key. "CURRENT" for current version; version history in S3. */
890
+ sk: {
891
+ type: "string",
892
+ required: true,
893
+ default: "CURRENT"
894
+ },
895
+ tenantId: {
896
+ type: "string",
897
+ required: true
898
+ },
899
+ workspaceId: {
900
+ type: "string",
901
+ required: true
902
+ },
903
+ /** FHIR Resource.id; logical id in URL and PK. */
904
+ id: {
905
+ type: "string",
906
+ required: true
907
+ },
908
+ /** FHIR resource as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */
909
+ resource: {
910
+ type: "string",
911
+ required: true
912
+ },
913
+ /** Version id (e.g. ULID). Tracks current version; S3 history key. */
914
+ vid: {
915
+ type: "string",
916
+ required: true
917
+ },
918
+ lastUpdated: {
919
+ type: "string",
920
+ required: true
921
+ },
922
+ deleted: {
923
+ type: "boolean",
924
+ required: false
925
+ },
926
+ bundleId: {
927
+ type: "string",
928
+ required: false
929
+ },
930
+ msgId: {
931
+ type: "string",
932
+ required: false
933
+ }
934
+ };
935
+ function createDataEntity(entity, resourceTypeLabel) {
936
+ return new import_electrodb3.Entity({
937
+ model: {
938
+ entity,
939
+ service: "data",
940
+ version: "01"
941
+ },
942
+ attributes: dataEntityAttributes,
943
+ indexes: {
944
+ /** Base table: PK, SK (data store key names). PK is built from tenantId, workspaceId, id. */
945
+ record: {
946
+ pk: {
947
+ field: "PK",
948
+ composite: ["tenantId", "workspaceId", "id"],
949
+ template: `TID#\${tenantId}#WID#\${workspaceId}#RT#${resourceTypeLabel}#ID#\${id}`
950
+ },
951
+ sk: {
952
+ field: "SK",
953
+ composite: ["sk"]
954
+ }
955
+ },
956
+ /** GSI4 — Resource Type Index: list all resources of this type in a workspace (no scan). Used by list operations (e.g. GET /Patient). */
957
+ gsi4: {
958
+ index: "GSI4",
959
+ pk: {
960
+ field: "GSI4PK",
961
+ composite: ["tenantId", "workspaceId"],
962
+ template: `TID#\${tenantId}#WID#\${workspaceId}#RT#${resourceTypeLabel}`
963
+ },
964
+ sk: {
965
+ field: "GSI4SK",
966
+ composite: ["id"],
967
+ template: `ID#\${id}`
968
+ }
969
+ }
970
+ }
971
+ });
972
+ }
973
+
974
+ // src/data/dynamo/entities/data/account-entity.ts
975
+ var AccountEntity = createDataEntity("account", "Account");
976
+
977
+ // src/data/dynamo/entities/data/activity-definition-entity.ts
978
+ var ActivityDefinitionEntity = createDataEntity(
979
+ "activitydefinition",
980
+ "ActivityDefinition"
981
+ );
982
+
983
+ // src/data/dynamo/entities/data/adverse-event-entity.ts
984
+ var AdverseEventEntity = createDataEntity(
985
+ "adverseevent",
986
+ "AdverseEvent"
987
+ );
988
+
989
+ // src/data/dynamo/entities/data/allergy-intolerance-entity.ts
990
+ var AllergyIntoleranceEntity = createDataEntity(
991
+ "allergyintolerance",
992
+ "AllergyIntolerance"
993
+ );
994
+
995
+ // src/data/dynamo/entities/data/appointment-entity.ts
996
+ var AppointmentEntity = createDataEntity("appointment", "Appointment");
997
+
998
+ // src/data/dynamo/entities/data/appointment-response-entity.ts
999
+ var AppointmentResponseEntity = createDataEntity(
1000
+ "appointmentresponse",
1001
+ "AppointmentResponse"
1002
+ );
1003
+
1004
+ // src/data/dynamo/entities/data/audit-event-entity.ts
1005
+ var AuditEventEntity = createDataEntity("auditevent", "AuditEvent");
1006
+
1007
+ // src/data/dynamo/entities/data/basic-entity.ts
1008
+ var BasicEntity = createDataEntity("basic", "Basic");
1009
+
1010
+ // src/data/dynamo/entities/data/biologically-derived-product-entity.ts
1011
+ var BiologicallyDerivedProductEntity = createDataEntity(
1012
+ "biologicallyderivedproduct",
1013
+ "BiologicallyDerivedProduct"
1014
+ );
1015
+
1016
+ // src/data/dynamo/entities/data/body-structure-entity.ts
1017
+ var BodyStructureEntity = createDataEntity(
1018
+ "bodystructure",
1019
+ "BodyStructure"
1020
+ );
1021
+
1022
+ // src/data/dynamo/entities/data/care-plan-entity.ts
1023
+ var CarePlanEntity = createDataEntity("careplan", "CarePlan");
1024
+
1025
+ // src/data/dynamo/entities/data/care-team-entity.ts
1026
+ var CareTeamEntity = createDataEntity("careteam", "CareTeam");
1027
+
1028
+ // src/data/dynamo/entities/data/catalog-entry-entity.ts
1029
+ var CatalogEntryEntity = createDataEntity(
1030
+ "catalogentry",
1031
+ "CatalogEntry"
1032
+ );
1033
+
1034
+ // src/data/dynamo/entities/data/charge-item-definition-entity.ts
1035
+ var ChargeItemDefinitionEntity = createDataEntity(
1036
+ "chargeitemdefinition",
1037
+ "ChargeItemDefinition"
1038
+ );
1039
+
1040
+ // src/data/dynamo/entities/data/charge-item-entity.ts
1041
+ var ChargeItemEntity = createDataEntity("chargeitem", "ChargeItem");
1042
+
1043
+ // src/data/dynamo/entities/data/claim-entity.ts
1044
+ var ClaimEntity = createDataEntity("claim", "Claim");
1045
+
1046
+ // src/data/dynamo/entities/data/claim-response-entity.ts
1047
+ var ClaimResponseEntity = createDataEntity(
1048
+ "claimresponse",
1049
+ "ClaimResponse"
1050
+ );
1051
+
1052
+ // src/data/dynamo/entities/data/clinical-impression-entity.ts
1053
+ var ClinicalImpressionEntity = createDataEntity(
1054
+ "clinicalimpression",
1055
+ "ClinicalImpression"
1056
+ );
1057
+
1058
+ // src/data/dynamo/entities/data/communication-entity.ts
1059
+ var CommunicationEntity = createDataEntity(
1060
+ "communication",
1061
+ "Communication"
1062
+ );
1063
+
1064
+ // src/data/dynamo/entities/data/communication-request-entity.ts
1065
+ var CommunicationRequestEntity = createDataEntity(
1066
+ "communicationrequest",
1067
+ "CommunicationRequest"
1068
+ );
1069
+
1070
+ // src/data/dynamo/entities/data/composition-entity.ts
1071
+ var CompositionEntity = createDataEntity("composition", "Composition");
1072
+
1073
+ // src/data/dynamo/entities/data/condition-entity.ts
1074
+ var ConditionEntity = createDataEntity("condition", "Condition");
1075
+
1076
+ // src/data/dynamo/entities/data/consent-entity.ts
1077
+ var ConsentEntity = createDataEntity("consent", "Consent");
1078
+
1079
+ // src/data/dynamo/entities/data/contract-entity.ts
1080
+ var ContractEntity = createDataEntity("contract", "Contract");
1081
+
1082
+ // src/data/dynamo/entities/data/coverage-eligibility-request-entity.ts
1083
+ var CoverageEligibilityRequestEntity = createDataEntity(
1084
+ "coverageeligibilityrequest",
1085
+ "CoverageEligibilityRequest"
1086
+ );
1087
+
1088
+ // src/data/dynamo/entities/data/coverage-eligibility-response-entity.ts
1089
+ var CoverageEligibilityResponseEntity = createDataEntity(
1090
+ "coverageeligibilityresponse",
1091
+ "CoverageEligibilityResponse"
1092
+ );
1093
+
1094
+ // src/data/dynamo/entities/data/coverage-entity.ts
1095
+ var CoverageEntity = createDataEntity("coverage", "Coverage");
1096
+
1097
+ // src/data/dynamo/entities/data/detected-issue-entity.ts
1098
+ var DetectedIssueEntity = createDataEntity(
1099
+ "detectedissue",
1100
+ "DetectedIssue"
1101
+ );
1102
+
1103
+ // src/data/dynamo/entities/data/device-definition-entity.ts
1104
+ var DeviceDefinitionEntity = createDataEntity(
1105
+ "devicedefinition",
1106
+ "DeviceDefinition"
1107
+ );
1108
+
1109
+ // src/data/dynamo/entities/data/device-entity.ts
1110
+ var DeviceEntity = createDataEntity("device", "Device");
1111
+
1112
+ // src/data/dynamo/entities/data/device-metric-entity.ts
1113
+ var DeviceMetricEntity = createDataEntity(
1114
+ "devicemetric",
1115
+ "DeviceMetric"
1116
+ );
1117
+
1118
+ // src/data/dynamo/entities/data/device-request-entity.ts
1119
+ var DeviceRequestEntity = createDataEntity(
1120
+ "devicerequest",
1121
+ "DeviceRequest"
1122
+ );
1123
+
1124
+ // src/data/dynamo/entities/data/device-use-statement-entity.ts
1125
+ var DeviceUseStatementEntity = createDataEntity(
1126
+ "deviceusestatement",
1127
+ "DeviceUseStatement"
1128
+ );
1129
+
1130
+ // src/data/dynamo/entities/data/diagnostic-report-entity.ts
1131
+ var DiagnosticReportEntity = createDataEntity(
1132
+ "diagnosticreport",
1133
+ "DiagnosticReport"
1134
+ );
1135
+
1136
+ // src/data/dynamo/entities/data/document-manifest-entity.ts
1137
+ var DocumentManifestEntity = createDataEntity(
1138
+ "documentmanifest",
1139
+ "DocumentManifest"
1140
+ );
1141
+
1142
+ // src/data/dynamo/entities/data/document-reference-entity.ts
1143
+ var DocumentReferenceEntity = createDataEntity(
1144
+ "documentreference",
1145
+ "DocumentReference"
1146
+ );
1147
+
1148
+ // src/data/dynamo/entities/data/effect-evidence-synthesis-entity.ts
1149
+ var EffectEvidenceSynthesisEntity = createDataEntity(
1150
+ "effectevidencesynthesis",
1151
+ "EffectEvidenceSynthesis"
1152
+ );
1153
+
1154
+ // src/data/dynamo/entities/data/encounter-entity.ts
1155
+ var EncounterEntity = createDataEntity("encounter", "Encounter");
1156
+
1157
+ // src/data/dynamo/entities/data/endpoint-entity.ts
1158
+ var EndpointEntity = createDataEntity("endpoint", "Endpoint");
1159
+
1160
+ // src/data/dynamo/entities/data/enrollment-request-entity.ts
1161
+ var EnrollmentRequestEntity = createDataEntity(
1162
+ "enrollmentrequest",
1163
+ "EnrollmentRequest"
1164
+ );
1165
+
1166
+ // src/data/dynamo/entities/data/enrollment-response-entity.ts
1167
+ var EnrollmentResponseEntity = createDataEntity(
1168
+ "enrollmentresponse",
1169
+ "EnrollmentResponse"
1170
+ );
1171
+
1172
+ // src/data/dynamo/entities/data/episode-of-care-entity.ts
1173
+ var EpisodeOfCareEntity = createDataEntity(
1174
+ "episodeofcare",
1175
+ "EpisodeOfCare"
1176
+ );
1177
+
1178
+ // src/data/dynamo/entities/data/event-definition-entity.ts
1179
+ var EventDefinitionEntity = createDataEntity(
1180
+ "eventdefinition",
1181
+ "EventDefinition"
1182
+ );
1183
+
1184
+ // src/data/dynamo/entities/data/evidence-entity.ts
1185
+ var EvidenceEntity = createDataEntity("evidence", "Evidence");
1186
+
1187
+ // src/data/dynamo/entities/data/evidence-variable-entity.ts
1188
+ var EvidenceVariableEntity = createDataEntity(
1189
+ "evidencevariable",
1190
+ "EvidenceVariable"
1191
+ );
1192
+
1193
+ // src/data/dynamo/entities/data/explanation-of-benefit-entity.ts
1194
+ var ExplanationOfBenefitEntity = createDataEntity(
1195
+ "explanationofbenefit",
1196
+ "ExplanationOfBenefit"
1197
+ );
1198
+
1199
+ // src/data/dynamo/entities/data/family-member-history-entity.ts
1200
+ var FamilyMemberHistoryEntity = createDataEntity(
1201
+ "familymemberhistory",
1202
+ "FamilyMemberHistory"
1203
+ );
1204
+
1205
+ // src/data/dynamo/entities/data/flag-entity.ts
1206
+ var FlagEntity = createDataEntity("flag", "Flag");
1207
+
1208
+ // src/data/dynamo/entities/data/goal-entity.ts
1209
+ var GoalEntity = createDataEntity("goal", "Goal");
1210
+
1211
+ // src/data/dynamo/entities/data/group-entity.ts
1212
+ var GroupEntity = createDataEntity("group", "Group");
1213
+
1214
+ // src/data/dynamo/entities/data/guidance-response-entity.ts
1215
+ var GuidanceResponseEntity = createDataEntity(
1216
+ "guidanceresponse",
1217
+ "GuidanceResponse"
1218
+ );
1219
+
1220
+ // src/data/dynamo/entities/data/healthcare-service-entity.ts
1221
+ var HealthcareServiceEntity = createDataEntity(
1222
+ "healthcareservice",
1223
+ "HealthcareService"
1224
+ );
1225
+
1226
+ // src/data/dynamo/entities/data/imaging-study-entity.ts
1227
+ var ImagingStudyEntity = createDataEntity(
1228
+ "imagingstudy",
1229
+ "ImagingStudy"
1230
+ );
1231
+
1232
+ // src/data/dynamo/entities/data/immunization-entity.ts
1233
+ var ImmunizationEntity = createDataEntity(
1234
+ "immunization",
1235
+ "Immunization"
1236
+ );
1237
+
1238
+ // src/data/dynamo/entities/data/immunization-evaluation-entity.ts
1239
+ var ImmunizationEvaluationEntity = createDataEntity(
1240
+ "immunizationevaluation",
1241
+ "ImmunizationEvaluation"
1242
+ );
1243
+
1244
+ // src/data/dynamo/entities/data/immunization-recommendation-entity.ts
1245
+ var ImmunizationRecommendationEntity = createDataEntity(
1246
+ "immunizationrecommendation",
1247
+ "ImmunizationRecommendation"
1248
+ );
1249
+
1250
+ // src/data/dynamo/entities/data/insurance-plan-entity.ts
1251
+ var InsurancePlanEntity = createDataEntity(
1252
+ "insuranceplan",
1253
+ "InsurancePlan"
1254
+ );
1255
+
1256
+ // src/data/dynamo/entities/data/invoice-entity.ts
1257
+ var InvoiceEntity = createDataEntity("invoice", "Invoice");
1258
+
1259
+ // src/data/dynamo/entities/data/library-entity.ts
1260
+ var LibraryEntity = createDataEntity("library", "Library");
1261
+
1262
+ // src/data/dynamo/entities/data/linkage-entity.ts
1263
+ var LinkageEntity = createDataEntity("linkage", "Linkage");
1264
+
1265
+ // src/data/dynamo/entities/data/list-entity.ts
1266
+ var ListEntity = createDataEntity("list", "List");
1267
+
1268
+ // src/data/dynamo/entities/data/location-entity.ts
1269
+ var LocationEntity = createDataEntity("location", "Location");
1270
+
1271
+ // src/data/dynamo/entities/data/measure-entity.ts
1272
+ var MeasureEntity = createDataEntity("measure", "Measure");
1273
+
1274
+ // src/data/dynamo/entities/data/measure-report-entity.ts
1275
+ var MeasureReportEntity = createDataEntity(
1276
+ "measurereport",
1277
+ "MeasureReport"
1278
+ );
1279
+
1280
+ // src/data/dynamo/entities/data/media-entity.ts
1281
+ var MediaEntity = createDataEntity("media", "Media");
1282
+
1283
+ // src/data/dynamo/entities/data/medication-administration-entity.ts
1284
+ var MedicationAdministrationEntity = createDataEntity(
1285
+ "medicationadministration",
1286
+ "MedicationAdministration"
1287
+ );
1288
+
1289
+ // src/data/dynamo/entities/data/medication-dispense-entity.ts
1290
+ var MedicationDispenseEntity = createDataEntity(
1291
+ "medicationdispense",
1292
+ "MedicationDispense"
1293
+ );
1294
+
1295
+ // src/data/dynamo/entities/data/medication-entity.ts
1296
+ var MedicationEntity = createDataEntity("medication", "Medication");
1297
+
1298
+ // src/data/dynamo/entities/data/medication-knowledge-entity.ts
1299
+ var MedicationKnowledgeEntity = createDataEntity(
1300
+ "medicationknowledge",
1301
+ "MedicationKnowledge"
1302
+ );
1303
+
1304
+ // src/data/dynamo/entities/data/medication-request-entity.ts
1305
+ var MedicationRequestEntity = createDataEntity(
1306
+ "medicationrequest",
1307
+ "MedicationRequest"
1308
+ );
1309
+
1310
+ // src/data/dynamo/entities/data/medication-statement-entity.ts
1311
+ var MedicationStatementEntity = createDataEntity(
1312
+ "medicationstatement",
1313
+ "MedicationStatement"
1314
+ );
1315
+
1316
+ // src/data/dynamo/entities/data/message-header-entity.ts
1317
+ var MessageHeaderEntity = createDataEntity(
1318
+ "messageheader",
1319
+ "MessageHeader"
1320
+ );
1321
+
1322
+ // src/data/dynamo/entities/data/molecular-sequence-entity.ts
1323
+ var MolecularSequenceEntity = createDataEntity(
1324
+ "molecularsequence",
1325
+ "MolecularSequence"
1326
+ );
1327
+
1328
+ // src/data/dynamo/entities/data/nutrition-order-entity.ts
1329
+ var NutritionOrderEntity = createDataEntity(
1330
+ "nutritionorder",
1331
+ "NutritionOrder"
1332
+ );
1333
+
1334
+ // src/data/dynamo/entities/data/observation-entity.ts
1335
+ var ObservationEntity = createDataEntity("observation", "Observation");
1336
+
1337
+ // src/data/dynamo/entities/data/organization-affiliation-entity.ts
1338
+ var OrganizationAffiliationEntity = createDataEntity(
1339
+ "organizationaffiliation",
1340
+ "OrganizationAffiliation"
1341
+ );
1342
+
1343
+ // src/data/dynamo/entities/data/organization-entity.ts
1344
+ var OrganizationEntity = createDataEntity(
1345
+ "organization",
1346
+ "Organization"
1347
+ );
1348
+
1349
+ // src/data/dynamo/entities/data/patient-entity.ts
1350
+ var PatientEntity = createDataEntity("patient", "Patient");
1351
+
1352
+ // src/data/dynamo/entities/data/payment-notice-entity.ts
1353
+ var PaymentNoticeEntity = createDataEntity(
1354
+ "paymentnotice",
1355
+ "PaymentNotice"
1356
+ );
1357
+
1358
+ // src/data/dynamo/entities/data/payment-reconciliation-entity.ts
1359
+ var PaymentReconciliationEntity = createDataEntity(
1360
+ "paymentreconciliation",
1361
+ "PaymentReconciliation"
1362
+ );
1363
+
1364
+ // src/data/dynamo/entities/data/person-entity.ts
1365
+ var PersonEntity = createDataEntity("person", "Person");
1366
+
1367
+ // src/data/dynamo/entities/data/plan-definition-entity.ts
1368
+ var PlanDefinitionEntity = createDataEntity(
1369
+ "plandefinition",
1370
+ "PlanDefinition"
1371
+ );
1372
+
1373
+ // src/data/dynamo/entities/data/practitioner-entity.ts
1374
+ var PractitionerEntity = createDataEntity(
1375
+ "practitioner",
1376
+ "Practitioner"
1377
+ );
1378
+
1379
+ // src/data/dynamo/entities/data/practitioner-role-entity.ts
1380
+ var PractitionerRoleEntity = createDataEntity(
1381
+ "practitionerrole",
1382
+ "PractitionerRole"
1383
+ );
1384
+
1385
+ // src/data/dynamo/entities/data/procedure-entity.ts
1386
+ var ProcedureEntity = createDataEntity("procedure", "Procedure");
1387
+
1388
+ // src/data/dynamo/entities/data/provenance-entity.ts
1389
+ var ProvenanceEntity = createDataEntity("provenance", "Provenance");
1390
+
1391
+ // src/data/dynamo/entities/data/questionnaire-entity.ts
1392
+ var QuestionnaireEntity = createDataEntity(
1393
+ "questionnaire",
1394
+ "Questionnaire"
1395
+ );
1396
+
1397
+ // src/data/dynamo/entities/data/questionnaire-response-entity.ts
1398
+ var QuestionnaireResponseEntity = createDataEntity(
1399
+ "questionnaireresponse",
1400
+ "QuestionnaireResponse"
1401
+ );
1402
+
1403
+ // src/data/dynamo/entities/data/related-person-entity.ts
1404
+ var RelatedPersonEntity = createDataEntity(
1405
+ "relatedperson",
1406
+ "RelatedPerson"
1407
+ );
1408
+
1409
+ // src/data/dynamo/entities/data/request-group-entity.ts
1410
+ var RequestGroupEntity = createDataEntity(
1411
+ "requestgroup",
1412
+ "RequestGroup"
1413
+ );
1414
+
1415
+ // src/data/dynamo/entities/data/research-study-entity.ts
1416
+ var ResearchStudyEntity = createDataEntity(
1417
+ "researchstudy",
1418
+ "ResearchStudy"
1419
+ );
1420
+
1421
+ // src/data/dynamo/entities/data/research-subject-entity.ts
1422
+ var ResearchSubjectEntity = createDataEntity(
1423
+ "researchsubject",
1424
+ "ResearchSubject"
1425
+ );
1426
+
1427
+ // src/data/dynamo/entities/data/risk-assessment-entity.ts
1428
+ var RiskAssessmentEntity = createDataEntity(
1429
+ "riskassessment",
1430
+ "RiskAssessment"
1431
+ );
1432
+
1433
+ // src/data/dynamo/entities/data/risk-evidence-synthesis-entity.ts
1434
+ var RiskEvidenceSynthesisEntity = createDataEntity(
1435
+ "riskevidencesynthesis",
1436
+ "RiskEvidenceSynthesis"
1437
+ );
1438
+
1439
+ // src/data/dynamo/entities/data/schedule-entity.ts
1440
+ var ScheduleEntity = createDataEntity("schedule", "Schedule");
1441
+
1442
+ // src/data/dynamo/entities/data/service-request-entity.ts
1443
+ var ServiceRequestEntity = createDataEntity(
1444
+ "servicerequest",
1445
+ "ServiceRequest"
1446
+ );
1447
+
1448
+ // src/data/dynamo/entities/data/slot-entity.ts
1449
+ var SlotEntity = createDataEntity("slot", "Slot");
1450
+
1451
+ // src/data/dynamo/entities/data/specimen-entity.ts
1452
+ var SpecimenEntity = createDataEntity("specimen", "Specimen");
1453
+
1454
+ // src/data/dynamo/entities/data/subscription-entity.ts
1455
+ var SubscriptionEntity = createDataEntity(
1456
+ "subscription",
1457
+ "Subscription"
1458
+ );
1459
+
1460
+ // src/data/dynamo/entities/data/substance-entity.ts
1461
+ var SubstanceEntity = createDataEntity("substance", "Substance");
1462
+
1463
+ // src/data/dynamo/entities/data/supply-delivery-entity.ts
1464
+ var SupplyDeliveryEntity = createDataEntity(
1465
+ "supplydelivery",
1466
+ "SupplyDelivery"
1467
+ );
1468
+
1469
+ // src/data/dynamo/entities/data/supply-request-entity.ts
1470
+ var SupplyRequestEntity = createDataEntity(
1471
+ "supplyrequest",
1472
+ "SupplyRequest"
1473
+ );
1474
+
1475
+ // src/data/dynamo/entities/data/task-entity.ts
1476
+ var TaskEntity = createDataEntity("task", "Task");
1477
+
1478
+ // src/data/dynamo/entities/data/verification-result-entity.ts
1479
+ var VerificationResultEntity = createDataEntity(
1480
+ "verificationresult",
1481
+ "VerificationResult"
1482
+ );
1483
+
1484
+ // src/data/dynamo/entities/data/vision-prescription-entity.ts
1485
+ var VisionPrescriptionEntity = createDataEntity(
1486
+ "visionprescription",
1487
+ "VisionPrescription"
1488
+ );
1489
+
1490
+ // src/data/dynamo/dynamo-data-service.ts
1491
+ var dataPlaneEntities = {
1492
+ account: AccountEntity,
1493
+ activitydefinition: ActivityDefinitionEntity,
1494
+ adverseevent: AdverseEventEntity,
1495
+ allergyintolerance: AllergyIntoleranceEntity,
1496
+ auditevent: AuditEventEntity,
1497
+ basic: BasicEntity,
1498
+ biologicallyderivedproduct: BiologicallyDerivedProductEntity,
1499
+ bodystructure: BodyStructureEntity,
1500
+ appointment: AppointmentEntity,
1501
+ appointmentresponse: AppointmentResponseEntity,
1502
+ careplan: CarePlanEntity,
1503
+ careteam: CareTeamEntity,
1504
+ catalogentry: CatalogEntryEntity,
1505
+ chargeitem: ChargeItemEntity,
1506
+ chargeitemdefinition: ChargeItemDefinitionEntity,
1507
+ claim: ClaimEntity,
1508
+ claimresponse: ClaimResponseEntity,
1509
+ clinicalimpression: ClinicalImpressionEntity,
1510
+ communication: CommunicationEntity,
1511
+ communicationrequest: CommunicationRequestEntity,
1512
+ composition: CompositionEntity,
1513
+ condition: ConditionEntity,
1514
+ consent: ConsentEntity,
1515
+ contract: ContractEntity,
1516
+ coverage: CoverageEntity,
1517
+ coverageeligibilityrequest: CoverageEligibilityRequestEntity,
1518
+ coverageeligibilityresponse: CoverageEligibilityResponseEntity,
1519
+ detectedissue: DetectedIssueEntity,
1520
+ device: DeviceEntity,
1521
+ devicedefinition: DeviceDefinitionEntity,
1522
+ devicemetric: DeviceMetricEntity,
1523
+ devicerequest: DeviceRequestEntity,
1524
+ deviceusestatement: DeviceUseStatementEntity,
1525
+ diagnosticreport: DiagnosticReportEntity,
1526
+ documentmanifest: DocumentManifestEntity,
1527
+ documentreference: DocumentReferenceEntity,
1528
+ effectevidencesynthesis: EffectEvidenceSynthesisEntity,
1529
+ encounter: EncounterEntity,
1530
+ endpoint: EndpointEntity,
1531
+ enrollmentrequest: EnrollmentRequestEntity,
1532
+ enrollmentresponse: EnrollmentResponseEntity,
1533
+ episodeofcare: EpisodeOfCareEntity,
1534
+ eventdefinition: EventDefinitionEntity,
1535
+ evidence: EvidenceEntity,
1536
+ evidencevariable: EvidenceVariableEntity,
1537
+ explanationofbenefit: ExplanationOfBenefitEntity,
1538
+ familymemberhistory: FamilyMemberHistoryEntity,
1539
+ flag: FlagEntity,
1540
+ goal: GoalEntity,
1541
+ group: GroupEntity,
1542
+ guidanceresponse: GuidanceResponseEntity,
1543
+ healthcareservice: HealthcareServiceEntity,
1544
+ immunization: ImmunizationEntity,
1545
+ immunizationevaluation: ImmunizationEvaluationEntity,
1546
+ immunizationrecommendation: ImmunizationRecommendationEntity,
1547
+ imagingstudy: ImagingStudyEntity,
1548
+ insuranceplan: InsurancePlanEntity,
1549
+ invoice: InvoiceEntity,
1550
+ library: LibraryEntity,
1551
+ linkage: LinkageEntity,
1552
+ list: ListEntity,
1553
+ location: LocationEntity,
1554
+ medication: MedicationEntity,
1555
+ medicationadministration: MedicationAdministrationEntity,
1556
+ medicationdispense: MedicationDispenseEntity,
1557
+ medicationknowledge: MedicationKnowledgeEntity,
1558
+ medicationrequest: MedicationRequestEntity,
1559
+ medicationstatement: MedicationStatementEntity,
1560
+ media: MediaEntity,
1561
+ measure: MeasureEntity,
1562
+ measurereport: MeasureReportEntity,
1563
+ messageheader: MessageHeaderEntity,
1564
+ molecularsequence: MolecularSequenceEntity,
1565
+ nutritionorder: NutritionOrderEntity,
1566
+ observation: ObservationEntity,
1567
+ organization: OrganizationEntity,
1568
+ organizationaffiliation: OrganizationAffiliationEntity,
1569
+ patient: PatientEntity,
1570
+ paymentnotice: PaymentNoticeEntity,
1571
+ paymentreconciliation: PaymentReconciliationEntity,
1572
+ person: PersonEntity,
1573
+ plandefinition: PlanDefinitionEntity,
1574
+ practitioner: PractitionerEntity,
1575
+ practitionerrole: PractitionerRoleEntity,
1576
+ procedure: ProcedureEntity,
1577
+ provenance: ProvenanceEntity,
1578
+ questionnaire: QuestionnaireEntity,
1579
+ questionnaireresponse: QuestionnaireResponseEntity,
1580
+ requestgroup: RequestGroupEntity,
1581
+ relatedperson: RelatedPersonEntity,
1582
+ researchstudy: ResearchStudyEntity,
1583
+ researchsubject: ResearchSubjectEntity,
1584
+ riskassessment: RiskAssessmentEntity,
1585
+ riskevidencesynthesis: RiskEvidenceSynthesisEntity,
1586
+ schedule: ScheduleEntity,
1587
+ servicerequest: ServiceRequestEntity,
1588
+ specimen: SpecimenEntity,
1589
+ substance: SubstanceEntity,
1590
+ subscription: SubscriptionEntity,
1591
+ supplydelivery: SupplyDeliveryEntity,
1592
+ supplyrequest: SupplyRequestEntity,
1593
+ slot: SlotEntity,
1594
+ task: TaskEntity,
1595
+ visionprescription: VisionPrescriptionEntity,
1596
+ verificationresult: VerificationResultEntity
1597
+ };
1598
+ var dataPlaneService = new import_electrodb4.Service(dataPlaneEntities, {
1599
+ table: defaultTableName,
1600
+ client: dynamoClient
1601
+ });
1602
+ var DynamoDataService = {
1603
+ entities: dataPlaneService.entities
1604
+ };
1605
+ function getDynamoDataService(tableName) {
1606
+ const resolved = tableName ?? defaultTableName;
1607
+ const service = new import_electrodb4.Service(dataPlaneEntities, {
1608
+ table: resolved,
1609
+ client: dynamoClient
1610
+ });
1611
+ return {
1612
+ entities: service.entities
1613
+ };
1614
+ }
1615
+
1616
+ // src/data/operations/data-operations-common.ts
1617
+ var DATA_ENTITY_SK = "CURRENT";
1618
+ async function getDataEntityById(entity, tenantId, workspaceId, id, resourceLabel) {
1619
+ const result = await entity.get({
1620
+ tenantId,
1621
+ workspaceId,
1622
+ id,
1623
+ sk: DATA_ENTITY_SK
1624
+ }).go();
1625
+ if (!result.data) {
1626
+ throw new NotFoundError(`${resourceLabel} ${id} not found`, {
1627
+ details: { id }
1628
+ });
1629
+ }
1630
+ const parsed = JSON.parse(decompressResource(result.data.resource));
1631
+ return {
1632
+ id: result.data.id,
1633
+ resource: { ...parsed, id: result.data.id }
1634
+ };
1635
+ }
1636
+ async function deleteDataEntityById(entity, tenantId, workspaceId, id) {
1637
+ await entity.delete({
1638
+ tenantId,
1639
+ workspaceId,
1640
+ id,
1641
+ sk: DATA_ENTITY_SK
1642
+ }).go();
1643
+ }
1644
+ async function listDataEntitiesByWorkspace(entity, tenantId, workspaceId) {
1645
+ const result = await entity.query.gsi4({ tenantId, workspaceId }).go();
1646
+ const items = result.data ?? [];
1647
+ const entries = items.map((item) => {
1648
+ const parsed = JSON.parse(decompressResource(item.resource));
825
1649
  return {
826
- errorResponse: res.status(400).json({
827
- resourceType: "OperationOutcome",
828
- issue: [
829
- {
830
- severity: "error",
831
- code: "invalid",
832
- diagnostics: "Request body must be a JSON object."
833
- }
834
- ]
835
- })
1650
+ id: item.id,
1651
+ resource: { ...parsed, id: item.id }
836
1652
  };
1653
+ });
1654
+ return { entries };
1655
+ }
1656
+ async function createDataEntityRecord(entity, tenantId, workspaceId, id, resourceWithAudit, fallbackDate) {
1657
+ const lastUpdated = resourceWithAudit.meta?.lastUpdated ?? fallbackDate ?? (/* @__PURE__ */ new Date()).toISOString();
1658
+ const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
1659
+ await entity.put({
1660
+ sk: DATA_ENTITY_SK,
1661
+ tenantId,
1662
+ workspaceId,
1663
+ id,
1664
+ resource: compressResource(JSON.stringify(resourceWithAudit)),
1665
+ vid,
1666
+ lastUpdated
1667
+ }).go();
1668
+ return {
1669
+ id,
1670
+ resource: resourceWithAudit
1671
+ };
1672
+ }
1673
+ async function updateDataEntityById(entity, tenantId, workspaceId, id, resourceLabel, context, buildPatched) {
1674
+ const existing = await entity.get({
1675
+ tenantId,
1676
+ workspaceId,
1677
+ id,
1678
+ sk: DATA_ENTITY_SK
1679
+ }).go();
1680
+ if (!existing.data) {
1681
+ throw new NotFoundError(`${resourceLabel} ${id} not found`, {
1682
+ details: { id }
1683
+ });
1684
+ }
1685
+ const existingStr = decompressResource(existing.data.resource);
1686
+ const { resource, lastUpdated } = buildPatched(existingStr);
1687
+ await entity.patch({
1688
+ tenantId,
1689
+ workspaceId,
1690
+ id,
1691
+ sk: DATA_ENTITY_SK
1692
+ }).set({
1693
+ resource: compressResource(JSON.stringify(resource)),
1694
+ lastUpdated
1695
+ }).go();
1696
+ return {
1697
+ id,
1698
+ resource
1699
+ };
1700
+ }
1701
+
1702
+ // src/data/operations/data/encounter/encounter-create-operation.ts
1703
+ async function createEncounterOperation(params) {
1704
+ const { context, body, id: optionalId, tableName } = params;
1705
+ const { tenantId, workspaceId, date, actorId, actorName } = context;
1706
+ const id = body.id ?? optionalId ?? (0, import_ulid.ulid)();
1707
+ const meta = {
1708
+ ...body.meta ?? {},
1709
+ lastUpdated: date,
1710
+ versionId: "1"
1711
+ };
1712
+ const encounterWithAudit = {
1713
+ ...body,
1714
+ resourceType: "Encounter",
1715
+ id,
1716
+ meta: mergeAuditIntoMeta(meta, {
1717
+ createdDate: date,
1718
+ createdById: actorId,
1719
+ createdByName: actorName,
1720
+ modifiedDate: date,
1721
+ modifiedById: actorId,
1722
+ modifiedByName: actorName
1723
+ })
1724
+ };
1725
+ const service = getDynamoDataService(tableName);
1726
+ return createDataEntityRecord(
1727
+ service.entities.encounter,
1728
+ tenantId,
1729
+ workspaceId,
1730
+ id,
1731
+ encounterWithAudit,
1732
+ date
1733
+ );
1734
+ }
1735
+
1736
+ // src/data/rest-api/routes/data/encounter/encounter-create-route.ts
1737
+ async function createEncounterRoute(req, res) {
1738
+ const bodyResult = requireJsonBody(req, res);
1739
+ if ("errorResponse" in bodyResult) return bodyResult.errorResponse;
1740
+ const ctx = req.openhiContext;
1741
+ const body = bodyResult.body;
1742
+ const encounter = {
1743
+ ...body,
1744
+ resourceType: "Encounter"
1745
+ };
1746
+ try {
1747
+ const result = await createEncounterOperation({
1748
+ context: ctx,
1749
+ body: encounter
1750
+ });
1751
+ return res.status(201).location(`${BASE_PATH.ENCOUNTER}/${result.id}`).json(result.resource);
1752
+ } catch (err) {
1753
+ console.error("POST Encounter error:", err);
1754
+ return res.status(500).json({
1755
+ resourceType: "OperationOutcome",
1756
+ issue: [
1757
+ { severity: "error", code: "exception", diagnostics: String(err) }
1758
+ ]
1759
+ });
1760
+ }
1761
+ }
1762
+
1763
+ // src/data/operations/data/encounter/encounter-delete-operation.ts
1764
+ async function deleteEncounterOperation(params) {
1765
+ const { context, id, tableName } = params;
1766
+ const { tenantId, workspaceId } = context;
1767
+ const service = getDynamoDataService(tableName);
1768
+ await deleteDataEntityById(
1769
+ service.entities.encounter,
1770
+ tenantId,
1771
+ workspaceId,
1772
+ id
1773
+ );
1774
+ }
1775
+
1776
+ // src/data/rest-api/routes/data/encounter/encounter-delete-route.ts
1777
+ async function deleteEncounterRoute(req, res) {
1778
+ const id = String(req.params.id);
1779
+ const ctx = req.openhiContext;
1780
+ try {
1781
+ await deleteEncounterOperation({ context: ctx, id });
1782
+ return res.status(204).send();
1783
+ } catch (err) {
1784
+ return sendOperationOutcome500(res, err, "DELETE Encounter error:");
1785
+ }
1786
+ }
1787
+
1788
+ // src/data/operations/data/encounter/encounter-get-by-id-operation.ts
1789
+ async function getEncounterByIdOperation(params) {
1790
+ const { context, id, tableName } = params;
1791
+ const { tenantId, workspaceId } = context;
1792
+ const service = getDynamoDataService(tableName);
1793
+ return getDataEntityById(
1794
+ service.entities.encounter,
1795
+ tenantId,
1796
+ workspaceId,
1797
+ id,
1798
+ "Encounter"
1799
+ );
1800
+ }
1801
+
1802
+ // src/data/rest-api/routes/data/encounter/encounter-get-by-id-route.ts
1803
+ async function getEncounterByIdRoute(req, res) {
1804
+ const id = String(req.params.id);
1805
+ const ctx = req.openhiContext;
1806
+ try {
1807
+ const result = await getEncounterByIdOperation({ context: ctx, id });
1808
+ return res.json(result.resource);
1809
+ } catch (err) {
1810
+ const status = domainErrorToHttpStatus(err);
1811
+ if (status === 404) {
1812
+ const diagnostics = err instanceof NotFoundError ? err.message : `Encounter ${id} not found`;
1813
+ return sendOperationOutcome404(res, diagnostics);
1814
+ }
1815
+ return sendOperationOutcome500(res, err, "GET Encounter error:");
1816
+ }
1817
+ }
1818
+
1819
+ // src/data/operations/data/encounter/encounter-list-operation.ts
1820
+ async function listEncountersOperation(params) {
1821
+ const { context, tableName } = params;
1822
+ const { tenantId, workspaceId } = context;
1823
+ const service = getDynamoDataService(tableName);
1824
+ return listDataEntitiesByWorkspace(
1825
+ service.entities.encounter,
1826
+ tenantId,
1827
+ workspaceId
1828
+ );
1829
+ }
1830
+
1831
+ // src/data/rest-api/routes/data/encounter/encounter-list-route.ts
1832
+ async function listEncountersRoute(req, res) {
1833
+ const ctx = req.openhiContext;
1834
+ try {
1835
+ const result = await listEncountersOperation({ context: ctx });
1836
+ const bundle = buildSearchsetBundle(BASE_PATH.ENCOUNTER, result.entries);
1837
+ return res.json(bundle);
1838
+ } catch (err) {
1839
+ return sendOperationOutcome500(res, err, "GET /Encounter list error:");
1840
+ }
1841
+ }
1842
+
1843
+ // src/data/operations/data/encounter/encounter-update-operation.ts
1844
+ async function updateEncounterOperation(params) {
1845
+ const { context, id, body, tableName } = params;
1846
+ const { tenantId, workspaceId, date, actorId, actorName } = context;
1847
+ const service = getDynamoDataService(tableName);
1848
+ return updateDataEntityById(
1849
+ service.entities.encounter,
1850
+ tenantId,
1851
+ workspaceId,
1852
+ id,
1853
+ "Encounter",
1854
+ context,
1855
+ (existingResourceStr) => {
1856
+ const bodyWithResource = body;
1857
+ const existingMeta = JSON.parse(existingResourceStr).meta;
1858
+ const encounter = {
1859
+ ...body,
1860
+ resourceType: "Encounter",
1861
+ id,
1862
+ meta: {
1863
+ ...bodyWithResource.meta ?? {},
1864
+ lastUpdated: date,
1865
+ versionId: "2"
1866
+ }
1867
+ };
1868
+ const encounterWithMeta = {
1869
+ ...encounter,
1870
+ meta: mergeAuditIntoMeta(encounter.meta ?? existingMeta, {
1871
+ modifiedDate: date,
1872
+ modifiedById: actorId,
1873
+ modifiedByName: actorName
1874
+ })
1875
+ };
1876
+ return {
1877
+ resource: encounterWithMeta,
1878
+ lastUpdated: date
1879
+ };
1880
+ }
1881
+ );
1882
+ }
1883
+
1884
+ // src/data/rest-api/routes/data/encounter/encounter-update-route.ts
1885
+ async function updateEncounterRoute(req, res) {
1886
+ const bodyResult = requireJsonBody(req, res);
1887
+ if ("errorResponse" in bodyResult) return bodyResult.errorResponse;
1888
+ const id = String(req.params.id);
1889
+ const ctx = req.openhiContext;
1890
+ const body = bodyResult.body;
1891
+ const encounter = {
1892
+ ...body,
1893
+ resourceType: "Encounter",
1894
+ id,
1895
+ meta: {
1896
+ ...body?.meta ?? {},
1897
+ lastUpdated: ctx.date,
1898
+ versionId: "2"
1899
+ }
1900
+ };
1901
+ try {
1902
+ const result = await updateEncounterOperation({
1903
+ context: ctx,
1904
+ id,
1905
+ body: encounter
1906
+ });
1907
+ return res.json(result.resource);
1908
+ } catch (err) {
1909
+ const status = domainErrorToHttpStatus(err);
1910
+ if (status === 404) {
1911
+ const diagnostics = err instanceof NotFoundError ? err.message : `Encounter ${id} not found`;
1912
+ return sendOperationOutcome404(res, diagnostics);
1913
+ }
1914
+ return sendOperationOutcome500(res, err, "PUT Encounter error:");
837
1915
  }
838
- return { body: raw };
839
1916
  }
840
1917
 
1918
+ // src/data/rest-api/routes/data/encounter/encounter.ts
1919
+ var router2 = import_express2.default.Router();
1920
+ router2.get("/", listEncountersRoute);
1921
+ router2.get("/:id", getEncounterByIdRoute);
1922
+ router2.post("/", createEncounterRoute);
1923
+ router2.put("/:id", updateEncounterRoute);
1924
+ router2.delete("/:id", deleteEncounterRoute);
1925
+
1926
+ // src/data/rest-api/routes/data/patient/patient.ts
1927
+ var import_express3 = __toESM(require("express"));
1928
+
1929
+ // src/data/operations/data/patient/patient-create-operation.ts
1930
+ var import_ulid2 = require("ulid");
1931
+
841
1932
  // src/data/import-patient.ts
842
1933
  var import_node_fs = require("fs");
843
1934
  var import_node_path = require("path");
844
- var OPENHI_EXT = "http://openhi.org/fhir/StructureDefinition";
845
- function mergeAuditIntoMeta(meta, audit) {
846
- const existing = meta ?? {};
847
- const ext = [
848
- ...Array.isArray(existing.extension) ? existing.extension : []
849
- ];
850
- const byUrl = new Map(ext.map((e) => [e.url, e]));
851
- function set(url, value, type) {
852
- if (value == null) return;
853
- byUrl.set(url, { url, [type]: value });
854
- }
855
- set(`${OPENHI_EXT}/created-date`, audit.createdDate, "valueDateTime");
856
- set(`${OPENHI_EXT}/created-by-id`, audit.createdById, "valueString");
857
- set(`${OPENHI_EXT}/created-by-name`, audit.createdByName, "valueString");
858
- set(`${OPENHI_EXT}/modified-date`, audit.modifiedDate, "valueDateTime");
859
- set(`${OPENHI_EXT}/modified-by-id`, audit.modifiedById, "valueString");
860
- set(`${OPENHI_EXT}/modified-by-name`, audit.modifiedByName, "valueString");
861
- set(`${OPENHI_EXT}/deleted-date`, audit.deletedDate, "valueDateTime");
862
- set(`${OPENHI_EXT}/deleted-by-id`, audit.deletedById, "valueString");
863
- set(`${OPENHI_EXT}/deleted-by-name`, audit.deletedByName, "valueString");
864
- return { ...existing, extension: Array.from(byUrl.values()) };
865
- }
866
1935
  function extractPatient(parsed) {
867
1936
  if (parsed && typeof parsed === "object" && "resourceType" in parsed) {
868
1937
  const root = parsed;
@@ -885,7 +1954,7 @@ function extractPatient(parsed) {
885
1954
  "File must be a FHIR Patient resource or a Bundle containing at least one Patient entry"
886
1955
  );
887
1956
  }
888
- var SK3 = "CURRENT";
1957
+ var SK5 = "CURRENT";
889
1958
  var defaultAudit = {
890
1959
  createdDate: (/* @__PURE__ */ new Date()).toISOString(),
891
1960
  createdById: "import",
@@ -923,7 +1992,7 @@ function patientToPutAttrs(patient, options) {
923
1992
  patientWithMeta.meta.lastUpdated = lastUpdated;
924
1993
  }
925
1994
  return {
926
- sk: SK3,
1995
+ sk: SK5,
927
1996
  tenantId,
928
1997
  workspaceId,
929
1998
  id: patient.id,
@@ -979,23 +2048,21 @@ if (require.main === module) {
979
2048
  void main();
980
2049
  }
981
2050
 
982
- // src/data/rest-api/routes/patient/patient-create.ts
983
- async function createPatient(req, res) {
984
- const bodyResult = requireJsonBody(req, res);
985
- if ("errorResponse" in bodyResult) return bodyResult.errorResponse;
986
- const ctx = req.openhiContext;
987
- const { tenantId, workspaceId, date, actorId, actorName } = ctx;
988
- const body = bodyResult.body;
989
- const id = body?.id ?? `patient-${Date.now()}`;
2051
+ // src/data/operations/data/patient/patient-create-operation.ts
2052
+ async function createPatientOperation(params) {
2053
+ const { context, body, id: optionalId } = params;
2054
+ const { tenantId, workspaceId, date, actorId, actorName } = context;
2055
+ const id = body.id ?? optionalId ?? (0, import_ulid2.ulid)();
2056
+ const meta = {
2057
+ ...body.meta ?? {},
2058
+ lastUpdated: date,
2059
+ versionId: "1"
2060
+ };
990
2061
  const patient = {
991
2062
  ...body,
992
2063
  resourceType: "Patient",
993
2064
  id,
994
- meta: {
995
- ...body?.meta ?? {},
996
- lastUpdated: date,
997
- versionId: "1"
998
- }
2065
+ meta
999
2066
  };
1000
2067
  const options = {
1001
2068
  tenantId,
@@ -1007,13 +2074,31 @@ async function createPatient(req, res) {
1007
2074
  modifiedById: actorId,
1008
2075
  modifiedByName: actorName
1009
2076
  };
1010
- const service = getDynamoDataService();
2077
+ const service = getDynamoDataService(params.tableName);
2078
+ const attrs = patientToPutAttrs(patient, options);
2079
+ await service.entities.patient.put(attrs).go();
2080
+ return {
2081
+ id,
2082
+ resource: patient
2083
+ };
2084
+ }
2085
+
2086
+ // src/data/rest-api/routes/data/patient/patient-create-route.ts
2087
+ async function createPatientRoute(req, res) {
2088
+ const bodyResult = requireJsonBody(req, res);
2089
+ if ("errorResponse" in bodyResult) return bodyResult.errorResponse;
2090
+ const ctx = req.openhiContext;
2091
+ const body = bodyResult.body;
2092
+ const patient = {
2093
+ ...body,
2094
+ resourceType: "Patient"
2095
+ };
1011
2096
  try {
1012
- const attrs = patientToPutAttrs(patient, options);
1013
- await service.entities.patient.put(
1014
- attrs
1015
- ).go();
1016
- return res.status(201).location(`${BASE_PATH3}/${id}`).json(patient);
2097
+ const result = await createPatientOperation({
2098
+ context: ctx,
2099
+ body: patient
2100
+ });
2101
+ return res.status(201).location(`${BASE_PATH.PATIENT}/${result.id}`).json(result.resource);
1017
2102
  } catch (err) {
1018
2103
  console.error("POST Patient error:", err);
1019
2104
  return res.status(500).json({
@@ -1025,180 +2110,409 @@ async function createPatient(req, res) {
1025
2110
  }
1026
2111
  }
1027
2112
 
1028
- // src/data/rest-api/routes/patient/patient-delete.ts
1029
- async function deletePatient(req, res) {
2113
+ // src/data/operations/data/patient/patient-delete-operation.ts
2114
+ async function deletePatientOperation(params) {
2115
+ const { context, id, tableName } = params;
2116
+ const { tenantId, workspaceId } = context;
2117
+ const service = getDynamoDataService(tableName);
2118
+ await deleteDataEntityById(
2119
+ service.entities.patient,
2120
+ tenantId,
2121
+ workspaceId,
2122
+ id
2123
+ );
2124
+ }
2125
+
2126
+ // src/data/rest-api/routes/data/patient/patient-delete-route.ts
2127
+ async function deletePatientRoute(req, res) {
1030
2128
  const id = String(req.params.id);
1031
- const { tenantId, workspaceId } = req.openhiContext;
1032
- const service = getDynamoDataService();
2129
+ const ctx = req.openhiContext;
1033
2130
  try {
1034
- await service.entities.patient.delete({ tenantId, workspaceId, id, sk: SK2 }).go();
2131
+ await deletePatientOperation({ context: ctx, id });
1035
2132
  return res.status(204).send();
1036
2133
  } catch (err) {
1037
- console.error("DELETE Patient error:", err);
1038
- return res.status(500).json({
1039
- resourceType: "OperationOutcome",
1040
- issue: [
1041
- { severity: "error", code: "exception", diagnostics: String(err) }
1042
- ]
1043
- });
2134
+ return sendOperationOutcome500(res, err, "DELETE Patient error:");
1044
2135
  }
1045
2136
  }
1046
2137
 
1047
- // src/data/rest-api/routes/patient/patient-get-by-id.ts
1048
- async function getPatientById(req, res) {
2138
+ // src/data/operations/data/patient/patient-get-by-id-operation.ts
2139
+ async function getPatientByIdOperation(params) {
2140
+ const { context, id, tableName } = params;
2141
+ const { tenantId, workspaceId } = context;
2142
+ const service = getDynamoDataService(tableName);
2143
+ return getDataEntityById(
2144
+ service.entities.patient,
2145
+ tenantId,
2146
+ workspaceId,
2147
+ id,
2148
+ "Patient"
2149
+ );
2150
+ }
2151
+
2152
+ // src/data/rest-api/routes/data/patient/patient-get-by-id-route.ts
2153
+ async function getPatientByIdRoute(req, res) {
1049
2154
  const id = String(req.params.id);
1050
- const { tenantId, workspaceId } = req.openhiContext;
1051
- const service = getDynamoDataService();
2155
+ const ctx = req.openhiContext;
1052
2156
  try {
1053
- const result = await service.entities.patient.get({ tenantId, workspaceId, id, sk: SK2 }).go();
1054
- if (!result.data) {
1055
- return res.status(404).json({
1056
- resourceType: "OperationOutcome",
1057
- issue: [
1058
- {
1059
- severity: "error",
1060
- code: "not-found",
1061
- diagnostics: `Patient ${id} not found`
1062
- }
1063
- ]
1064
- });
2157
+ const result = await getPatientByIdOperation({ context: ctx, id });
2158
+ return res.json(result.resource);
2159
+ } catch (err) {
2160
+ const status = domainErrorToHttpStatus(err);
2161
+ if (status === 404) {
2162
+ const diagnostics = err instanceof NotFoundError ? err.message : `Patient ${id} not found`;
2163
+ return sendOperationOutcome404(res, diagnostics);
1065
2164
  }
1066
- const resource = JSON.parse(
1067
- decompressResource(result.data.resource)
1068
- );
1069
- return res.json({ ...resource, id: result.data.id });
2165
+ return sendOperationOutcome500(res, err, "GET Patient error:");
2166
+ }
2167
+ }
2168
+
2169
+ // src/data/operations/data/patient/patient-list-operation.ts
2170
+ async function listPatientsOperation(params) {
2171
+ const { context, tableName } = params;
2172
+ const { tenantId, workspaceId } = context;
2173
+ const service = getDynamoDataService(tableName);
2174
+ return listDataEntitiesByWorkspace(
2175
+ service.entities.patient,
2176
+ tenantId,
2177
+ workspaceId
2178
+ );
2179
+ }
2180
+
2181
+ // src/data/rest-api/routes/data/patient/patient-list-route.ts
2182
+ async function listPatientsRoute(req, res) {
2183
+ const ctx = req.openhiContext;
2184
+ try {
2185
+ const result = await listPatientsOperation({ context: ctx });
2186
+ const bundle = buildSearchsetBundle(BASE_PATH.PATIENT, result.entries);
2187
+ return res.json(bundle);
1070
2188
  } catch (err) {
1071
- console.error("GET Patient error:", err);
1072
- return res.status(500).json({
1073
- resourceType: "OperationOutcome",
1074
- issue: [
1075
- {
1076
- severity: "error",
1077
- code: "exception",
1078
- diagnostics: String(err)
2189
+ return sendOperationOutcome500(res, err, "GET /Patient list error:");
2190
+ }
2191
+ }
2192
+
2193
+ // src/data/operations/data/patient/patient-update-operation.ts
2194
+ async function updatePatientOperation(params) {
2195
+ const { context, id, body, tableName } = params;
2196
+ const { tenantId, workspaceId, date, actorId, actorName } = context;
2197
+ const service = getDynamoDataService(tableName);
2198
+ return updateDataEntityById(
2199
+ service.entities.patient,
2200
+ tenantId,
2201
+ workspaceId,
2202
+ id,
2203
+ "Patient",
2204
+ context,
2205
+ (existingResourceStr) => {
2206
+ const bodyWithResource = body;
2207
+ const existingMeta = JSON.parse(existingResourceStr).meta;
2208
+ const patient = {
2209
+ ...body,
2210
+ resourceType: "Patient",
2211
+ id,
2212
+ meta: {
2213
+ ...bodyWithResource.meta ?? {},
2214
+ lastUpdated: date,
2215
+ versionId: "2"
1079
2216
  }
1080
- ]
2217
+ };
2218
+ const patientWithMeta = {
2219
+ ...patient,
2220
+ meta: mergeAuditIntoMeta(patient.meta ?? existingMeta, {
2221
+ modifiedDate: date,
2222
+ modifiedById: actorId,
2223
+ modifiedByName: actorName
2224
+ })
2225
+ };
2226
+ return {
2227
+ resource: patientWithMeta,
2228
+ lastUpdated: date
2229
+ };
2230
+ }
2231
+ );
2232
+ }
2233
+
2234
+ // src/data/rest-api/routes/data/patient/patient-update-route.ts
2235
+ async function updatePatientRoute(req, res) {
2236
+ const bodyResult = requireJsonBody(req, res);
2237
+ if ("errorResponse" in bodyResult) return bodyResult.errorResponse;
2238
+ const id = String(req.params.id);
2239
+ const ctx = req.openhiContext;
2240
+ const body = bodyResult.body;
2241
+ const patient = {
2242
+ ...body,
2243
+ resourceType: "Patient",
2244
+ id
2245
+ };
2246
+ try {
2247
+ const result = await updatePatientOperation({
2248
+ context: ctx,
2249
+ id,
2250
+ body: patient
1081
2251
  });
2252
+ return res.json(result.resource);
2253
+ } catch (err) {
2254
+ const status = domainErrorToHttpStatus(err);
2255
+ if (status === 404) {
2256
+ const diagnostics = err instanceof NotFoundError ? err.message : `Patient ${id} not found`;
2257
+ return sendOperationOutcome404(res, diagnostics);
2258
+ }
2259
+ return sendOperationOutcome500(res, err, "PUT Patient error:");
1082
2260
  }
1083
2261
  }
1084
2262
 
1085
- // src/data/rest-api/routes/patient/patient-list.ts
1086
- async function listPatients(req, res) {
1087
- const { tenantId, workspaceId } = req.openhiContext;
1088
- const service = getDynamoDataService();
2263
+ // src/data/rest-api/routes/data/patient/patient.ts
2264
+ var router3 = import_express3.default.Router();
2265
+ router3.get("/", listPatientsRoute);
2266
+ router3.get("/:id", getPatientByIdRoute);
2267
+ router3.post("/", createPatientRoute);
2268
+ router3.put("/:id", updatePatientRoute);
2269
+ router3.delete("/:id", deletePatientRoute);
2270
+
2271
+ // src/data/rest-api/routes/data/practitioner/practitioner.ts
2272
+ var import_express4 = __toESM(require("express"));
2273
+
2274
+ // src/data/operations/data/practitioner/practitioner-create-operation.ts
2275
+ var import_ulid3 = require("ulid");
2276
+ async function createPractitionerOperation(params) {
2277
+ const { context, body, id: optionalId, tableName } = params;
2278
+ const { tenantId, workspaceId, date, actorId, actorName } = context;
2279
+ const id = body.id ?? optionalId ?? (0, import_ulid3.ulid)();
2280
+ const meta = {
2281
+ ...body.meta ?? {},
2282
+ lastUpdated: date,
2283
+ versionId: "1"
2284
+ };
2285
+ const practitionerWithAudit = {
2286
+ ...body,
2287
+ resourceType: "Practitioner",
2288
+ id,
2289
+ meta: mergeAuditIntoMeta(meta, {
2290
+ createdDate: date,
2291
+ createdById: actorId,
2292
+ createdByName: actorName,
2293
+ modifiedDate: date,
2294
+ modifiedById: actorId,
2295
+ modifiedByName: actorName
2296
+ })
2297
+ };
2298
+ const service = getDynamoDataService(tableName);
2299
+ return createDataEntityRecord(
2300
+ service.entities.practitioner,
2301
+ tenantId,
2302
+ workspaceId,
2303
+ id,
2304
+ practitionerWithAudit,
2305
+ date
2306
+ );
2307
+ }
2308
+
2309
+ // src/data/rest-api/routes/data/practitioner/practitioner-create-route.ts
2310
+ async function createPractitionerRoute(req, res) {
2311
+ const bodyResult = requireJsonBody(req, res);
2312
+ if ("errorResponse" in bodyResult) return bodyResult.errorResponse;
2313
+ const ctx = req.openhiContext;
2314
+ const body = bodyResult.body;
2315
+ const practitioner = {
2316
+ ...body,
2317
+ resourceType: "Practitioner"
2318
+ };
1089
2319
  try {
1090
- const result = await service.entities.patient.query.gsi4({ tenantId, workspaceId }).go();
1091
- const entries = (result.data ?? []).map((item) => {
1092
- const resource = JSON.parse(decompressResource(item.resource));
1093
- return {
1094
- fullUrl: `${BASE_PATH3}/${item.id}`,
1095
- resource: { ...resource, id: item.id }
1096
- };
2320
+ const result = await createPractitionerOperation({
2321
+ context: ctx,
2322
+ body: practitioner
1097
2323
  });
1098
- const bundle = {
1099
- resourceType: "Bundle",
1100
- type: "searchset",
1101
- total: entries.length,
1102
- link: [{ relation: "self", url: BASE_PATH3 }],
1103
- entry: entries
1104
- };
1105
- return res.json(bundle);
2324
+ return res.status(201).location(`${BASE_PATH.PRACTITIONER}/${result.id}`).json(result.resource);
1106
2325
  } catch (err) {
1107
- console.error("GET /Patient list error:", err);
2326
+ console.error("POST Practitioner error:", err);
1108
2327
  return res.status(500).json({
1109
2328
  resourceType: "OperationOutcome",
1110
2329
  issue: [
1111
- {
1112
- severity: "error",
1113
- code: "exception",
1114
- diagnostics: String(err)
1115
- }
2330
+ { severity: "error", code: "exception", diagnostics: String(err) }
1116
2331
  ]
1117
2332
  });
1118
2333
  }
1119
2334
  }
1120
2335
 
1121
- // src/data/rest-api/routes/patient/patient-update.ts
1122
- async function updatePatient(req, res) {
2336
+ // src/data/operations/data/practitioner/practitioner-delete-operation.ts
2337
+ async function deletePractitionerOperation(params) {
2338
+ const { context, id, tableName } = params;
2339
+ const { tenantId, workspaceId } = context;
2340
+ const service = getDynamoDataService(tableName);
2341
+ await deleteDataEntityById(
2342
+ service.entities.practitioner,
2343
+ tenantId,
2344
+ workspaceId,
2345
+ id
2346
+ );
2347
+ }
2348
+
2349
+ // src/data/rest-api/routes/data/practitioner/practitioner-delete-route.ts
2350
+ async function deletePractitionerRoute(req, res) {
2351
+ const id = String(req.params.id);
2352
+ const ctx = req.openhiContext;
2353
+ try {
2354
+ await deletePractitionerOperation({ context: ctx, id });
2355
+ return res.status(204).send();
2356
+ } catch (err) {
2357
+ return sendOperationOutcome500(res, err, "DELETE Practitioner error:");
2358
+ }
2359
+ }
2360
+
2361
+ // src/data/operations/data/practitioner/practitioner-get-by-id-operation.ts
2362
+ async function getPractitionerByIdOperation(params) {
2363
+ const { context, id, tableName } = params;
2364
+ const { tenantId, workspaceId } = context;
2365
+ const service = getDynamoDataService(tableName);
2366
+ return getDataEntityById(
2367
+ service.entities.practitioner,
2368
+ tenantId,
2369
+ workspaceId,
2370
+ id,
2371
+ "Practitioner"
2372
+ );
2373
+ }
2374
+
2375
+ // src/data/rest-api/routes/data/practitioner/practitioner-get-by-id-route.ts
2376
+ async function getPractitionerByIdRoute(req, res) {
2377
+ const id = String(req.params.id);
2378
+ const ctx = req.openhiContext;
2379
+ try {
2380
+ const result = await getPractitionerByIdOperation({ context: ctx, id });
2381
+ return res.json(result.resource);
2382
+ } catch (err) {
2383
+ const status = domainErrorToHttpStatus(err);
2384
+ if (status === 404) {
2385
+ const diagnostics = err instanceof NotFoundError ? err.message : `Practitioner ${id} not found`;
2386
+ return sendOperationOutcome404(res, diagnostics);
2387
+ }
2388
+ return sendOperationOutcome500(res, err, "GET Practitioner error:");
2389
+ }
2390
+ }
2391
+
2392
+ // src/data/operations/data/practitioner/practitioner-list-operation.ts
2393
+ async function listPractitionersOperation(params) {
2394
+ const { context, tableName } = params;
2395
+ const { tenantId, workspaceId } = context;
2396
+ const service = getDynamoDataService(tableName);
2397
+ return listDataEntitiesByWorkspace(
2398
+ service.entities.practitioner,
2399
+ tenantId,
2400
+ workspaceId
2401
+ );
2402
+ }
2403
+
2404
+ // src/data/rest-api/routes/data/practitioner/practitioner-list-route.ts
2405
+ async function listPractitionersRoute(req, res) {
2406
+ const ctx = req.openhiContext;
2407
+ try {
2408
+ const result = await listPractitionersOperation({ context: ctx });
2409
+ const bundle = buildSearchsetBundle(BASE_PATH.PRACTITIONER, result.entries);
2410
+ return res.json(bundle);
2411
+ } catch (err) {
2412
+ return sendOperationOutcome500(res, err, "GET /Practitioner list error:");
2413
+ }
2414
+ }
2415
+
2416
+ // src/data/operations/data/practitioner/practitioner-update-operation.ts
2417
+ async function updatePractitionerOperation(params) {
2418
+ const { context, id, body, tableName } = params;
2419
+ const { tenantId, workspaceId, date, actorId, actorName } = context;
2420
+ const service = getDynamoDataService(tableName);
2421
+ return updateDataEntityById(
2422
+ service.entities.practitioner,
2423
+ tenantId,
2424
+ workspaceId,
2425
+ id,
2426
+ "Practitioner",
2427
+ context,
2428
+ (existingResourceStr) => {
2429
+ const bodyWithResource = body;
2430
+ const existingMeta = JSON.parse(existingResourceStr).meta;
2431
+ const practitioner = {
2432
+ ...body,
2433
+ resourceType: "Practitioner",
2434
+ id,
2435
+ meta: {
2436
+ ...bodyWithResource.meta ?? {},
2437
+ lastUpdated: date,
2438
+ versionId: "2"
2439
+ }
2440
+ };
2441
+ const practitionerWithMeta = {
2442
+ ...practitioner,
2443
+ meta: mergeAuditIntoMeta(practitioner.meta ?? existingMeta, {
2444
+ modifiedDate: date,
2445
+ modifiedById: actorId,
2446
+ modifiedByName: actorName
2447
+ })
2448
+ };
2449
+ return {
2450
+ resource: practitionerWithMeta,
2451
+ lastUpdated: date
2452
+ };
2453
+ }
2454
+ );
2455
+ }
2456
+
2457
+ // src/data/rest-api/routes/data/practitioner/practitioner-update-route.ts
2458
+ async function updatePractitionerRoute(req, res) {
1123
2459
  const bodyResult = requireJsonBody(req, res);
1124
2460
  if ("errorResponse" in bodyResult) return bodyResult.errorResponse;
1125
2461
  const id = String(req.params.id);
1126
2462
  const ctx = req.openhiContext;
1127
- const { tenantId, workspaceId, date, actorId, actorName } = ctx;
1128
2463
  const body = bodyResult.body;
1129
- const patient = {
2464
+ const practitioner = {
1130
2465
  ...body,
1131
- resourceType: "Patient",
2466
+ resourceType: "Practitioner",
1132
2467
  id,
1133
2468
  meta: {
1134
2469
  ...body?.meta ?? {},
1135
- lastUpdated: date,
2470
+ lastUpdated: ctx.date,
1136
2471
  versionId: "2"
1137
2472
  }
1138
2473
  };
1139
- const service = getDynamoDataService();
1140
2474
  try {
1141
- const existing = await service.entities.patient.get({ tenantId, workspaceId, id, sk: SK2 }).go();
1142
- if (!existing.data) {
1143
- return res.status(404).json({
1144
- resourceType: "OperationOutcome",
1145
- issue: [
1146
- {
1147
- severity: "error",
1148
- code: "not-found",
1149
- diagnostics: `Patient ${id} not found`
1150
- }
1151
- ]
1152
- });
1153
- }
1154
- const existingMeta = existing.data.resource != null ? JSON.parse(decompressResource(existing.data.resource)).meta : void 0;
1155
- const patientWithMeta = {
1156
- ...patient,
1157
- meta: mergeAuditIntoMeta(
1158
- patient.meta ?? existingMeta,
1159
- {
1160
- modifiedDate: date,
1161
- modifiedById: actorId,
1162
- modifiedByName: actorName
1163
- }
1164
- )
1165
- };
1166
- await service.entities.patient.patch({ tenantId, workspaceId, id, sk: SK2 }).set({
1167
- resource: compressResource(JSON.stringify(patientWithMeta)),
1168
- lastUpdated: date
1169
- }).go();
1170
- return res.json(patientWithMeta);
1171
- } catch (err) {
1172
- console.error("PUT Patient error:", err);
1173
- return res.status(500).json({
1174
- resourceType: "OperationOutcome",
1175
- issue: [
1176
- { severity: "error", code: "exception", diagnostics: String(err) }
1177
- ]
2475
+ const result = await updatePractitionerOperation({
2476
+ context: ctx,
2477
+ id,
2478
+ body: practitioner
1178
2479
  });
2480
+ return res.json(result.resource);
2481
+ } catch (err) {
2482
+ const status = domainErrorToHttpStatus(err);
2483
+ if (status === 404) {
2484
+ const diagnostics = err instanceof NotFoundError ? err.message : `Practitioner ${id} not found`;
2485
+ return sendOperationOutcome404(res, diagnostics);
2486
+ }
2487
+ return sendOperationOutcome500(res, err, "PUT Practitioner error:");
1179
2488
  }
1180
2489
  }
1181
2490
 
1182
- // src/data/rest-api/routes/patient/patient.ts
1183
- var router2 = import_express2.default.Router();
1184
- router2.get("/", listPatients);
1185
- router2.get("/:id", getPatientById);
1186
- router2.post("/", createPatient);
1187
- router2.put("/:id", updatePatient);
1188
- router2.delete("/:id", deletePatient);
2491
+ // src/data/rest-api/routes/data/practitioner/practitioner.ts
2492
+ var router4 = import_express4.default.Router();
2493
+ router4.get("/", listPractitionersRoute);
2494
+ router4.get("/:id", getPractitionerByIdRoute);
2495
+ router4.post("/", createPractitionerRoute);
2496
+ router4.put("/:id", updatePractitionerRoute);
2497
+ router4.delete("/:id", deletePractitionerRoute);
1189
2498
 
1190
2499
  // src/data/rest-api/rest-api.ts
1191
- var app = (0, import_express3.default)();
2500
+ var app = (0, import_express5.default)();
1192
2501
  app.set("view engine", "ejs");
1193
2502
  app.set("views", import_node_path2.default.join(__dirname, "views"));
1194
- app.use(import_express3.default.json());
1195
- app.use(import_express3.default.urlencoded({ extended: true }));
2503
+ app.use(import_express5.default.json());
2504
+ app.use(import_express5.default.urlencoded({ extended: true }));
1196
2505
  app.use(normalizeJsonBodyMiddleware);
1197
2506
  app.get("/", (_req, res) => {
1198
2507
  return res.status(200).json({ message: "POC App is running" });
1199
2508
  });
1200
- app.use(["/Patient", "/Configuration"], openHiContextMiddleware);
1201
- app.use("/Patient", router2);
2509
+ app.use(
2510
+ ["/Patient", "/Encounter", "/Practitioner", "/Configuration"],
2511
+ openHiContextMiddleware
2512
+ );
2513
+ app.use("/Patient", router3);
2514
+ app.use("/Encounter", router2);
2515
+ app.use("/Practitioner", router4);
1202
2516
  app.use("/Configuration", router);
1203
2517
 
1204
2518
  // src/data/lambda/rest-api-lambda.handler.ts