@openhi/constructs 0.0.31 → 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.
- package/lib/index.d.mts +12 -29
- package/lib/index.d.ts +12 -29
- package/lib/index.js +6 -22
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +6 -23
- package/lib/index.mjs.map +1 -1
- package/lib/rest-api-lambda.handler.js +1818 -504
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +1818 -504
- package/lib/rest-api-lambda.handler.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -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
|
|
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
|
|
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
|
|
180
|
+
var ConfigurationEntity = new import_electrodb.Entity({
|
|
175
181
|
model: {
|
|
176
182
|
entity: "configuration",
|
|
177
|
-
service: "
|
|
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: "
|
|
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/
|
|
281
|
-
var
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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/
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
} =
|
|
486
|
-
const
|
|
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
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
525
|
-
|
|
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/
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
const
|
|
546
|
-
const { tenantId, workspaceId, actorId, roleId: ctxRoleId } =
|
|
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 =
|
|
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
|
|
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/
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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 =
|
|
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
|
|
572
|
-
|
|
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 ${
|
|
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
|
|
631
|
+
const client = new import_client_ssm.SSMClient({ region });
|
|
628
632
|
try {
|
|
629
|
-
const describeResult = await
|
|
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
|
|
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
|
|
719
|
-
const
|
|
720
|
-
const
|
|
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
|
|
723
|
-
|
|
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 = [...
|
|
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/
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const
|
|
762
|
-
const { tenantId, workspaceId, actorId, date, roleId: ctxRoleId } =
|
|
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
|
|
765
|
-
const
|
|
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
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
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 ${
|
|
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
|
-
{
|
|
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("/",
|
|
811
|
-
router.get("/:
|
|
812
|
-
router.post("/",
|
|
813
|
-
router.put("/:
|
|
814
|
-
router.delete("/:
|
|
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/
|
|
853
|
+
// src/data/rest-api/routes/data/encounter/encounter.ts
|
|
817
854
|
var import_express2 = __toESM(require("express"));
|
|
818
855
|
|
|
819
|
-
// src/data/
|
|
820
|
-
var
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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
|
-
|
|
827
|
-
|
|
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
|
|
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:
|
|
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/
|
|
983
|
-
async function
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
const
|
|
987
|
-
const
|
|
988
|
-
|
|
989
|
-
|
|
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
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
)
|
|
1016
|
-
return res.status(201).location(`${
|
|
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/
|
|
1029
|
-
async function
|
|
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
|
|
1032
|
-
const service = getDynamoDataService();
|
|
2129
|
+
const ctx = req.openhiContext;
|
|
1033
2130
|
try {
|
|
1034
|
-
await
|
|
2131
|
+
await deletePatientOperation({ context: ctx, id });
|
|
1035
2132
|
return res.status(204).send();
|
|
1036
2133
|
} catch (err) {
|
|
1037
|
-
|
|
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/
|
|
1048
|
-
async function
|
|
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
|
|
1051
|
-
const service = getDynamoDataService();
|
|
2155
|
+
const ctx = req.openhiContext;
|
|
1052
2156
|
try {
|
|
1053
|
-
const result = await
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
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
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
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
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
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
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
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
|
|
1091
|
-
|
|
1092
|
-
|
|
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
|
-
|
|
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("
|
|
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/
|
|
1122
|
-
async function
|
|
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
|
|
2464
|
+
const practitioner = {
|
|
1130
2465
|
...body,
|
|
1131
|
-
resourceType: "
|
|
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
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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/
|
|
1183
|
-
var
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
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,
|
|
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(
|
|
1195
|
-
app.use(
|
|
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(
|
|
1201
|
-
|
|
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
|