@openhi/constructs 0.0.0
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/README.md +3 -0
- package/lib/app/index.d.ts +4 -0
- package/lib/app/index.js +21 -0
- package/lib/app/open-hi-app.d.ts +85 -0
- package/lib/app/open-hi-app.js +127 -0
- package/lib/app/open-hi-environment.d.ts +59 -0
- package/lib/app/open-hi-environment.js +72 -0
- package/lib/app/open-hi-service.d.ts +169 -0
- package/lib/app/open-hi-service.js +195 -0
- package/lib/app/open-hi-stage.d.ts +71 -0
- package/lib/app/open-hi-stage.js +70 -0
- package/lib/components/acm/root-wildcard-certificate.d.ts +15 -0
- package/lib/components/acm/root-wildcard-certificate.js +35 -0
- package/lib/components/api-gateway/core-http-api.d.ts +10 -0
- package/lib/components/api-gateway/core-http-api.js +44 -0
- package/lib/components/api-gateway/http-lambda-integration-no-permissions.d.ts +18 -0
- package/lib/components/api-gateway/http-lambda-integration-no-permissions.js +26 -0
- package/lib/components/app-sync/core-graphql-api.d.ts +12 -0
- package/lib/components/app-sync/core-graphql-api.js +54 -0
- package/lib/components/auth.d.ts +75 -0
- package/lib/components/auth.js +100 -0
- package/lib/components/cognito/core-user-pool-client.d.ts +10 -0
- package/lib/components/cognito/core-user-pool-client.js +47 -0
- package/lib/components/cognito/core-user-pool-domain.d.ts +10 -0
- package/lib/components/cognito/core-user-pool-domain.js +41 -0
- package/lib/components/cognito/core-user-pool-kms-key.d.ts +10 -0
- package/lib/components/cognito/core-user-pool-kms-key.js +37 -0
- package/lib/components/cognito/core-user-pool.d.ts +10 -0
- package/lib/components/cognito/core-user-pool.js +54 -0
- package/lib/components/core.d.ts +102 -0
- package/lib/components/core.js +79 -0
- package/lib/components/dynamodb/dynamo-db-data-store.d.ts +33 -0
- package/lib/components/dynamodb/dynamo-db-data-store.js +107 -0
- package/lib/components/event-bridge/data-event-bus.d.ts +19 -0
- package/lib/components/event-bridge/data-event-bus.js +34 -0
- package/lib/components/event-bridge/ops-event-bus.d.ts +19 -0
- package/lib/components/event-bridge/ops-event-bus.js +34 -0
- package/lib/components/global.d.ts +36 -0
- package/lib/components/global.js +63 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +18 -0
- package/lib/components/route-53/child-hosted-zone.d.ts +20 -0
- package/lib/components/route-53/child-hosted-zone.js +48 -0
- package/lib/components/route-53/root-hosted-zone.d.ts +10 -0
- package/lib/components/route-53/root-hosted-zone.js +20 -0
- package/lib/components/ssm/discoverable-string-parameter.d.ts +59 -0
- package/lib/components/ssm/discoverable-string-parameter.js +50 -0
- package/lib/components/ssm/index.d.ts +1 -0
- package/lib/components/ssm/index.js +18 -0
- package/lib/data/dynamo/ehr/r4/Patient.d.ts +180 -0
- package/lib/data/dynamo/ehr/r4/Patient.js +192 -0
- package/lib/data/dynamo/ehr/r4/ehr-r4-data-service.d.ts +162 -0
- package/lib/data/dynamo/ehr/r4/ehr-r4-data-service.js +37 -0
- package/lib/data/hello-world.d.ts +39 -0
- package/lib/data/hello-world.js +59 -0
- package/lib/data/import-patient-with-dynalite.d.ts +1 -0
- package/lib/data/import-patient-with-dynalite.js +87 -0
- package/lib/data/import-patient.d.ts +47 -0
- package/lib/data/import-patient.js +158 -0
- package/lib/data/lambda/rest-api-lambda.d.ts +13 -0
- package/lib/data/lambda/rest-api-lambda.handler.d.ts +1 -0
- package/lib/data/lambda/rest-api-lambda.handler.js +10 -0
- package/lib/data/lambda/rest-api-lambda.js +22 -0
- package/lib/data/middleware/open-hi-context.d.ts +13 -0
- package/lib/data/middleware/open-hi-context.js +31 -0
- package/lib/data/rest-api/ehr/r4/Patient.d.ts +16 -0
- package/lib/data/rest-api/ehr/r4/Patient.js +234 -0
- package/lib/data/rest-api/rest-api-local.d.ts +1 -0
- package/lib/data/rest-api/rest-api-local.js +8 -0
- package/lib/data/rest-api/rest-api-mockdata.d.ts +7 -0
- package/lib/data/rest-api/rest-api-mockdata.js +585 -0
- package/lib/data/rest-api/rest-api.d.ts +3 -0
- package/lib/data/rest-api/rest-api.js +26 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +20 -0
- package/lib/lib/compression.d.ts +27 -0
- package/lib/lib/compression.js +87 -0
- package/lib/services/index.d.ts +5 -0
- package/lib/services/index.js +22 -0
- package/lib/services/open-hi-auth-service.d.ts +31 -0
- package/lib/services/open-hi-auth-service.js +31 -0
- package/lib/services/open-hi-core-service.d.ts +15 -0
- package/lib/services/open-hi-core-service.js +38 -0
- package/lib/services/open-hi-data-service.d.ts +18 -0
- package/lib/services/open-hi-data-service.js +18 -0
- package/lib/services/open-hi-global-service.d.ts +15 -0
- package/lib/services/open-hi-global-service.js +44 -0
- package/lib/services/open-hi-rest-api-service.d.ts +17 -0
- package/lib/services/open-hi-rest-api-service.js +107 -0
- package/package.json +67 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EhrR4DataService = void 0;
|
|
4
|
+
exports.getEhrR4DataService = getEhrR4DataService;
|
|
5
|
+
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
6
|
+
const electrodb_1 = require("electrodb");
|
|
7
|
+
const Patient_1 = require("./Patient");
|
|
8
|
+
/**
|
|
9
|
+
* DynamoDB table name for the data store. Set via DYNAMO_TABLE_NAME at runtime
|
|
10
|
+
* (e.g. from Lambda env); defaults for local/test.
|
|
11
|
+
*/
|
|
12
|
+
const table = process.env.DYNAMO_TABLE_NAME ?? "jesttesttable";
|
|
13
|
+
/**
|
|
14
|
+
* DynamoDB client. When MOCK_DYNAMODB_ENDPOINT is set (e.g. local DynamoDB or
|
|
15
|
+
* jest-dynalite), uses that endpoint with no SSL and region "local".
|
|
16
|
+
*/
|
|
17
|
+
const client = new client_dynamodb_1.DynamoDBClient({
|
|
18
|
+
...(process.env.MOCK_DYNAMODB_ENDPOINT && {
|
|
19
|
+
endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
|
|
20
|
+
sslEnabled: false,
|
|
21
|
+
region: "local",
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
const entities = { patient: Patient_1.Patient };
|
|
25
|
+
/**
|
|
26
|
+
* ElectroDB Service for the single-table data store. Provides access to Patient
|
|
27
|
+
* and other entities; use with the data store table (PK, SK, GSI1, GSI2, GSI3, GSI4).
|
|
28
|
+
*/
|
|
29
|
+
exports.EhrR4DataService = new electrodb_1.Service(entities, { table, client });
|
|
30
|
+
/**
|
|
31
|
+
* Returns an ElectroDB Service for the data store using the given table name.
|
|
32
|
+
* Use in tests with a dedicated table (e.g. "data-store-test" in jest-dynalite).
|
|
33
|
+
*/
|
|
34
|
+
function getEhrR4DataService(tableName) {
|
|
35
|
+
return new electrodb_1.Service(entities, { table: tableName, client });
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWhyLXI0LWRhdGEtc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9kYXRhL2R5bmFtby9laHIvcjQvZWhyLXI0LWRhdGEtc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFrQ0Esa0RBSUM7QUF0Q0QsOERBQTBEO0FBQzFELHlDQUFvQztBQUNwQyx1Q0FBb0M7QUFFcEM7OztHQUdHO0FBQ0gsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsSUFBSSxlQUFlLENBQUM7QUFFL0Q7OztHQUdHO0FBQ0gsTUFBTSxNQUFNLEdBQUcsSUFBSSxnQ0FBYyxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixJQUFJO1FBQ3hDLFFBQVEsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQjtRQUM1QyxVQUFVLEVBQUUsS0FBSztRQUNqQixNQUFNLEVBQUUsT0FBTztLQUNoQixDQUFDO0NBQ0gsQ0FBQyxDQUFDO0FBRUgsTUFBTSxRQUFRLEdBQUcsRUFBRSxPQUFPLEVBQUUsaUJBQU8sRUFBRSxDQUFDO0FBRXRDOzs7R0FHRztBQUNVLFFBQUEsZ0JBQWdCLEdBQUcsSUFBSSxtQkFBTyxDQUFDLFFBQVEsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO0FBRXpFOzs7R0FHRztBQUNILFNBQWdCLG1CQUFtQixDQUNqQyxTQUFpQjtJQUVqQixPQUFPLElBQUksbUJBQU8sQ0FBQyxRQUFRLEVBQUUsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7QUFDN0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IER5bmFtb0RCQ2xpZW50IH0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1keW5hbW9kYlwiO1xuaW1wb3J0IHsgU2VydmljZSB9IGZyb20gXCJlbGVjdHJvZGJcIjtcbmltcG9ydCB7IFBhdGllbnQgfSBmcm9tIFwiLi9QYXRpZW50XCI7XG5cbi8qKlxuICogRHluYW1vREIgdGFibGUgbmFtZSBmb3IgdGhlIGRhdGEgc3RvcmUuIFNldCB2aWEgRFlOQU1PX1RBQkxFX05BTUUgYXQgcnVudGltZVxuICogKGUuZy4gZnJvbSBMYW1iZGEgZW52KTsgZGVmYXVsdHMgZm9yIGxvY2FsL3Rlc3QuXG4gKi9cbmNvbnN0IHRhYmxlID0gcHJvY2Vzcy5lbnYuRFlOQU1PX1RBQkxFX05BTUUgPz8gXCJqZXN0dGVzdHRhYmxlXCI7XG5cbi8qKlxuICogRHluYW1vREIgY2xpZW50LiBXaGVuIE1PQ0tfRFlOQU1PREJfRU5EUE9JTlQgaXMgc2V0IChlLmcuIGxvY2FsIER5bmFtb0RCIG9yXG4gKiBqZXN0LWR5bmFsaXRlKSwgdXNlcyB0aGF0IGVuZHBvaW50IHdpdGggbm8gU1NMIGFuZCByZWdpb24gXCJsb2NhbFwiLlxuICovXG5jb25zdCBjbGllbnQgPSBuZXcgRHluYW1vREJDbGllbnQoe1xuICAuLi4ocHJvY2Vzcy5lbnYuTU9DS19EWU5BTU9EQl9FTkRQT0lOVCAmJiB7XG4gICAgZW5kcG9pbnQ6IHByb2Nlc3MuZW52Lk1PQ0tfRFlOQU1PREJfRU5EUE9JTlQsXG4gICAgc3NsRW5hYmxlZDogZmFsc2UsXG4gICAgcmVnaW9uOiBcImxvY2FsXCIsXG4gIH0pLFxufSk7XG5cbmNvbnN0IGVudGl0aWVzID0geyBwYXRpZW50OiBQYXRpZW50IH07XG5cbi8qKlxuICogRWxlY3Ryb0RCIFNlcnZpY2UgZm9yIHRoZSBzaW5nbGUtdGFibGUgZGF0YSBzdG9yZS4gUHJvdmlkZXMgYWNjZXNzIHRvIFBhdGllbnRcbiAqIGFuZCBvdGhlciBlbnRpdGllczsgdXNlIHdpdGggdGhlIGRhdGEgc3RvcmUgdGFibGUgKFBLLCBTSywgR1NJMSwgR1NJMiwgR1NJMywgR1NJNCkuXG4gKi9cbmV4cG9ydCBjb25zdCBFaHJSNERhdGFTZXJ2aWNlID0gbmV3IFNlcnZpY2UoZW50aXRpZXMsIHsgdGFibGUsIGNsaWVudCB9KTtcblxuLyoqXG4gKiBSZXR1cm5zIGFuIEVsZWN0cm9EQiBTZXJ2aWNlIGZvciB0aGUgZGF0YSBzdG9yZSB1c2luZyB0aGUgZ2l2ZW4gdGFibGUgbmFtZS5cbiAqIFVzZSBpbiB0ZXN0cyB3aXRoIGEgZGVkaWNhdGVkIHRhYmxlIChlLmcuIFwiZGF0YS1zdG9yZS10ZXN0XCIgaW4gamVzdC1keW5hbGl0ZSkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRFaHJSNERhdGFTZXJ2aWNlKFxuICB0YWJsZU5hbWU6IHN0cmluZyxcbik6IHR5cGVvZiBFaHJSNERhdGFTZXJ2aWNlIHtcbiAgcmV0dXJuIG5ldyBTZXJ2aWNlKGVudGl0aWVzLCB7IHRhYmxlOiB0YWJsZU5hbWUsIGNsaWVudCB9KTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Entity, Service } from "electrodb";
|
|
2
|
+
export declare const FooModel: Service<{
|
|
3
|
+
user: Entity<string, string, string, {
|
|
4
|
+
model: {
|
|
5
|
+
entity: string;
|
|
6
|
+
service: string;
|
|
7
|
+
version: string;
|
|
8
|
+
};
|
|
9
|
+
attributes: {
|
|
10
|
+
/**
|
|
11
|
+
* Identifier
|
|
12
|
+
*/
|
|
13
|
+
FooId: {
|
|
14
|
+
type: "string";
|
|
15
|
+
readOnly: true;
|
|
16
|
+
required: true;
|
|
17
|
+
default: () => `${string}-${string}-${string}-${string}-${string}`;
|
|
18
|
+
};
|
|
19
|
+
FirstName: {
|
|
20
|
+
type: "string";
|
|
21
|
+
};
|
|
22
|
+
LastName: {
|
|
23
|
+
type: "string";
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
indexes: {
|
|
27
|
+
record: {
|
|
28
|
+
pk: {
|
|
29
|
+
field: string;
|
|
30
|
+
composite: "FooId"[];
|
|
31
|
+
};
|
|
32
|
+
sk: {
|
|
33
|
+
field: string;
|
|
34
|
+
composite: "FooId"[];
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}>;
|
|
39
|
+
}>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FooModel = void 0;
|
|
4
|
+
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
5
|
+
const electrodb_1 = require("electrodb");
|
|
6
|
+
/**
|
|
7
|
+
* Define the client and table
|
|
8
|
+
*/
|
|
9
|
+
const table = process.env.DYNAMO_TABLE_NAME || "jesttesttable";
|
|
10
|
+
const client = new client_dynamodb_1.DynamoDBClient({
|
|
11
|
+
...(process.env.MOCK_DYNAMODB_ENDPOINT && {
|
|
12
|
+
endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
|
|
13
|
+
sslEnabled: false,
|
|
14
|
+
region: "local",
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* One user in Cognito can be associated with multiple tenants.
|
|
19
|
+
*/
|
|
20
|
+
const User = new electrodb_1.Entity({
|
|
21
|
+
model: {
|
|
22
|
+
entity: "foo",
|
|
23
|
+
service: "foo",
|
|
24
|
+
version: "01",
|
|
25
|
+
},
|
|
26
|
+
attributes: {
|
|
27
|
+
/**
|
|
28
|
+
* Identifier
|
|
29
|
+
*/
|
|
30
|
+
FooId: {
|
|
31
|
+
type: "string",
|
|
32
|
+
readOnly: true,
|
|
33
|
+
required: true,
|
|
34
|
+
default: () => crypto.randomUUID(),
|
|
35
|
+
},
|
|
36
|
+
FirstName: {
|
|
37
|
+
type: "string",
|
|
38
|
+
},
|
|
39
|
+
LastName: {
|
|
40
|
+
type: "string",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
indexes: {
|
|
44
|
+
record: {
|
|
45
|
+
pk: {
|
|
46
|
+
field: "pk",
|
|
47
|
+
composite: ["FooId"],
|
|
48
|
+
},
|
|
49
|
+
sk: {
|
|
50
|
+
field: "sk",
|
|
51
|
+
composite: ["FooId"],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
exports.FooModel = new electrodb_1.Service({
|
|
57
|
+
user: User,
|
|
58
|
+
}, { table, client });
|
|
59
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8td29ybGQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZGF0YS9oZWxsby13b3JsZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw4REFBMEQ7QUFDMUQseUNBQTRDO0FBRTVDOztHQUVHO0FBQ0gsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsSUFBSSxlQUFlLENBQUM7QUFFL0QsTUFBTSxNQUFNLEdBQUcsSUFBSSxnQ0FBYyxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixJQUFJO1FBQ3hDLFFBQVEsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQjtRQUM1QyxVQUFVLEVBQUUsS0FBSztRQUNqQixNQUFNLEVBQUUsT0FBTztLQUNoQixDQUFDO0NBQ0gsQ0FBQyxDQUFDO0FBRUg7O0dBRUc7QUFDSCxNQUFNLElBQUksR0FBRyxJQUFJLGtCQUFNLENBQ3JCO0lBQ0UsS0FBSyxFQUFFO1FBQ0wsTUFBTSxFQUFFLEtBQUs7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLE9BQU8sRUFBRSxJQUFJO0tBQ2Q7SUFDRCxVQUFVLEVBQUU7UUFDVjs7V0FFRztRQUNILEtBQUssRUFBRTtZQUNMLElBQUksRUFBRSxRQUFRO1lBQ2QsUUFBUSxFQUFFLElBQUk7WUFDZCxRQUFRLEVBQUUsSUFBSTtZQUNkLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFO1NBQ25DO1FBQ0QsU0FBUyxFQUFFO1lBQ1QsSUFBSSxFQUFFLFFBQVE7U0FDZjtRQUNELFFBQVEsRUFBRTtZQUNSLElBQUksRUFBRSxRQUFRO1NBQ2Y7S0FDRjtJQUVELE9BQU8sRUFBRTtRQUNQLE1BQU0sRUFBRTtZQUNOLEVBQUUsRUFBRTtnQkFDRixLQUFLLEVBQUUsSUFBSTtnQkFDWCxTQUFTLEVBQUUsQ0FBQyxPQUFPLENBQUM7YUFDckI7WUFDRCxFQUFFLEVBQUU7Z0JBQ0YsS0FBSyxFQUFFLElBQUk7Z0JBQ1gsU0FBUyxFQUFFLENBQUMsT0FBTyxDQUFDO2FBQ3JCO1NBQ0Y7S0FDRjtDQUNGLENBRUYsQ0FBQztBQUVXLFFBQUEsUUFBUSxHQUFHLElBQUksbUJBQU8sQ0FDakM7SUFDRSxJQUFJLEVBQUUsSUFBSTtDQUNYLEVBQ0QsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLENBQ2xCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBEeW5hbW9EQkNsaWVudCB9IGZyb20gXCJAYXdzLXNkay9jbGllbnQtZHluYW1vZGJcIjtcbmltcG9ydCB7IEVudGl0eSwgU2VydmljZSB9IGZyb20gXCJlbGVjdHJvZGJcIjtcblxuLyoqXG4gKiBEZWZpbmUgdGhlIGNsaWVudCBhbmQgdGFibGVcbiAqL1xuY29uc3QgdGFibGUgPSBwcm9jZXNzLmVudi5EWU5BTU9fVEFCTEVfTkFNRSB8fCBcImplc3R0ZXN0dGFibGVcIjtcblxuY29uc3QgY2xpZW50ID0gbmV3IER5bmFtb0RCQ2xpZW50KHtcbiAgLi4uKHByb2Nlc3MuZW52Lk1PQ0tfRFlOQU1PREJfRU5EUE9JTlQgJiYge1xuICAgIGVuZHBvaW50OiBwcm9jZXNzLmVudi5NT0NLX0RZTkFNT0RCX0VORFBPSU5ULFxuICAgIHNzbEVuYWJsZWQ6IGZhbHNlLFxuICAgIHJlZ2lvbjogXCJsb2NhbFwiLFxuICB9KSxcbn0pO1xuXG4vKipcbiAqIE9uZSB1c2VyIGluIENvZ25pdG8gY2FuIGJlIGFzc29jaWF0ZWQgd2l0aCBtdWx0aXBsZSB0ZW5hbnRzLlxuICovXG5jb25zdCBVc2VyID0gbmV3IEVudGl0eShcbiAge1xuICAgIG1vZGVsOiB7XG4gICAgICBlbnRpdHk6IFwiZm9vXCIsXG4gICAgICBzZXJ2aWNlOiBcImZvb1wiLFxuICAgICAgdmVyc2lvbjogXCIwMVwiLFxuICAgIH0sXG4gICAgYXR0cmlidXRlczoge1xuICAgICAgLyoqXG4gICAgICAgKiBJZGVudGlmaWVyXG4gICAgICAgKi9cbiAgICAgIEZvb0lkOiB7XG4gICAgICAgIHR5cGU6IFwic3RyaW5nXCIsXG4gICAgICAgIHJlYWRPbmx5OiB0cnVlLFxuICAgICAgICByZXF1aXJlZDogdHJ1ZSxcbiAgICAgICAgZGVmYXVsdDogKCkgPT4gY3J5cHRvLnJhbmRvbVVVSUQoKSxcbiAgICAgIH0sXG4gICAgICBGaXJzdE5hbWU6IHtcbiAgICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgIH0sXG4gICAgICBMYXN0TmFtZToge1xuICAgICAgICB0eXBlOiBcInN0cmluZ1wiLFxuICAgICAgfSxcbiAgICB9LFxuXG4gICAgaW5kZXhlczoge1xuICAgICAgcmVjb3JkOiB7XG4gICAgICAgIHBrOiB7XG4gICAgICAgICAgZmllbGQ6IFwicGtcIixcbiAgICAgICAgICBjb21wb3NpdGU6IFtcIkZvb0lkXCJdLFxuICAgICAgICB9LFxuICAgICAgICBzazoge1xuICAgICAgICAgIGZpZWxkOiBcInNrXCIsXG4gICAgICAgICAgY29tcG9zaXRlOiBbXCJGb29JZFwiXSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSxcbiAgfSxcbiAgLyp7IHRhYmxlLCBjbGllbnQgfSwqL1xuKTtcblxuZXhwb3J0IGNvbnN0IEZvb01vZGVsID0gbmV3IFNlcnZpY2UoXG4gIHtcbiAgICB1c2VyOiBVc2VyLFxuICB9LFxuICB7IHRhYmxlLCBjbGllbnQgfSxcbik7XG4iXX0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const node_path_1 = require("node:path");
|
|
37
|
+
// eslint-disable-next-line import/no-extraneous-dependencies -- dev-only script using test infra (jest-dynalite)
|
|
38
|
+
const jest_dynalite_1 = require("jest-dynalite");
|
|
39
|
+
const DATA_STORE_TABLE = "data-store-test";
|
|
40
|
+
/**
|
|
41
|
+
* Runs the single-patient import against a local dynalite instance (same config as tests).
|
|
42
|
+
* Setup runs before importing ehr-r4-data-service so MOCK_DYNAMODB_ENDPOINT is set when the client is created.
|
|
43
|
+
* Usage: ts-node import-patient-with-dynalite.ts <path-to-patient.json> [tenantId] [workspaceId]
|
|
44
|
+
*/
|
|
45
|
+
async function main() {
|
|
46
|
+
const [, , fileArg, tenantId = "tenant-1", workspaceId = "ws-1"] = process.argv;
|
|
47
|
+
if (!fileArg) {
|
|
48
|
+
console.error("Usage: import-patient-with-dynalite.ts <path-to-patient.json> [tenantId] [workspaceId]");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
// Point jest-dynalite at the package root so it finds jest-dynalite-config.
|
|
52
|
+
// Must run before any import of ehr-r4-data-service so the DynamoDB client sees MOCK_DYNAMODB_ENDPOINT.
|
|
53
|
+
const packageRoot = (0, node_path_1.resolve)(__dirname, "../..");
|
|
54
|
+
(0, jest_dynalite_1.setup)(packageRoot);
|
|
55
|
+
await (0, jest_dynalite_1.startDb)();
|
|
56
|
+
await (0, jest_dynalite_1.createTables)();
|
|
57
|
+
const { importPatientFromFile } = await Promise.resolve().then(() => __importStar(require("./import-patient")));
|
|
58
|
+
const { getEhrR4DataService } = await Promise.resolve().then(() => __importStar(require("./dynamo/ehr/r4/ehr-r4-data-service")));
|
|
59
|
+
const sk = "CURRENT";
|
|
60
|
+
try {
|
|
61
|
+
const result = await importPatientFromFile(fileArg, {
|
|
62
|
+
tenantId,
|
|
63
|
+
workspaceId,
|
|
64
|
+
tableName: DATA_STORE_TABLE,
|
|
65
|
+
});
|
|
66
|
+
console.log(`Imported Patient ${result.id} (tenant=${result.tenantId}, workspace=${result.workspaceId})`);
|
|
67
|
+
const service = getEhrR4DataService(DATA_STORE_TABLE);
|
|
68
|
+
const getResult = await service.entities.patient
|
|
69
|
+
.get({ tenantId, workspaceId, id: result.id, sk })
|
|
70
|
+
.go();
|
|
71
|
+
if (getResult.data) {
|
|
72
|
+
console.log("Read back from dynalite:", JSON.stringify(getResult.data, null, 2));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.log("Read back: no record found");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
console.error(err);
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
await (0, jest_dynalite_1.stopDb)();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
void main();
|
|
87
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1wb3J0LXBhdGllbnQtd2l0aC1keW5hbGl0ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kYXRhL2ltcG9ydC1wYXRpZW50LXdpdGgtZHluYWxpdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSx5Q0FBb0M7QUFDcEMsaUhBQWlIO0FBQ2pILGlEQUFxRTtBQUVyRSxNQUFNLGdCQUFnQixHQUFHLGlCQUFpQixDQUFDO0FBRTNDOzs7O0dBSUc7QUFDSCxLQUFLLFVBQVUsSUFBSTtJQUNqQixNQUFNLENBQUMsRUFBRSxBQUFELEVBQUcsT0FBTyxFQUFFLFFBQVEsR0FBRyxVQUFVLEVBQUUsV0FBVyxHQUFHLE1BQU0sQ0FBQyxHQUM5RCxPQUFPLENBQUMsSUFBSSxDQUFDO0lBRWYsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2IsT0FBTyxDQUFDLEtBQUssQ0FDWCx3RkFBd0YsQ0FDekYsQ0FBQztRQUNGLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDbEIsQ0FBQztJQUVELDRFQUE0RTtJQUM1RSx3R0FBd0c7SUFDeEcsTUFBTSxXQUFXLEdBQUcsSUFBQSxtQkFBTyxFQUFDLFNBQVMsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNoRCxJQUFBLHFCQUFLLEVBQUMsV0FBVyxDQUFDLENBQUM7SUFFbkIsTUFBTSxJQUFBLHVCQUFPLEdBQUUsQ0FBQztJQUNoQixNQUFNLElBQUEsNEJBQVksR0FBRSxDQUFDO0lBRXJCLE1BQU0sRUFBRSxxQkFBcUIsRUFBRSxHQUFHLHdEQUFhLGtCQUFrQixHQUFDLENBQUM7SUFDbkUsTUFBTSxFQUFFLG1CQUFtQixFQUFFLEdBQzNCLHdEQUFhLHFDQUFxQyxHQUFDLENBQUM7SUFFdEQsTUFBTSxFQUFFLEdBQUcsU0FBUyxDQUFDO0lBRXJCLElBQUksQ0FBQztRQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0scUJBQXFCLENBQUMsT0FBTyxFQUFFO1lBQ2xELFFBQVE7WUFDUixXQUFXO1lBQ1gsU0FBUyxFQUFFLGdCQUFnQjtTQUM1QixDQUFDLENBQUM7UUFDSCxPQUFPLENBQUMsR0FBRyxDQUNULG9CQUFvQixNQUFNLENBQUMsRUFBRSxZQUFZLE1BQU0sQ0FBQyxRQUFRLGVBQWUsTUFBTSxDQUFDLFdBQVcsR0FBRyxDQUM3RixDQUFDO1FBRUYsTUFBTSxPQUFPLEdBQUcsbUJBQW1CLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUN0RCxNQUFNLFNBQVMsR0FBRyxNQUFNLE9BQU8sQ0FBQyxRQUFRLENBQUMsT0FBTzthQUM3QyxHQUFHLENBQUMsRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLEVBQUUsRUFBRSxNQUFNLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO2FBQ2pELEVBQUUsRUFBRSxDQUFDO1FBRVIsSUFBSSxTQUFTLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDbkIsT0FBTyxDQUFDLEdBQUcsQ0FDVCwwQkFBMEIsRUFDMUIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FDeEMsQ0FBQztRQUNKLENBQUM7YUFBTSxDQUFDO1lBQ04sT0FBTyxDQUFDLEdBQUcsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO1FBQzVDLENBQUM7SUFDSCxDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDbkIsT0FBTyxDQUFDLFFBQVEsR0FBRyxDQUFDLENBQUM7SUFDdkIsQ0FBQztZQUFTLENBQUM7UUFDVCxNQUFNLElBQUEsc0JBQU0sR0FBRSxDQUFDO0lBQ2pCLENBQUM7QUFDSCxDQUFDO0FBRUQsS0FBSyxJQUFJLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHJlc29sdmUgfSBmcm9tIFwibm9kZTpwYXRoXCI7XG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzIC0tIGRldi1vbmx5IHNjcmlwdCB1c2luZyB0ZXN0IGluZnJhIChqZXN0LWR5bmFsaXRlKVxuaW1wb3J0IHsgc2V0dXAsIHN0YXJ0RGIsIHN0b3BEYiwgY3JlYXRlVGFibGVzIH0gZnJvbSBcImplc3QtZHluYWxpdGVcIjtcblxuY29uc3QgREFUQV9TVE9SRV9UQUJMRSA9IFwiZGF0YS1zdG9yZS10ZXN0XCI7XG5cbi8qKlxuICogUnVucyB0aGUgc2luZ2xlLXBhdGllbnQgaW1wb3J0IGFnYWluc3QgYSBsb2NhbCBkeW5hbGl0ZSBpbnN0YW5jZSAoc2FtZSBjb25maWcgYXMgdGVzdHMpLlxuICogU2V0dXAgcnVucyBiZWZvcmUgaW1wb3J0aW5nIGVoci1yNC1kYXRhLXNlcnZpY2Ugc28gTU9DS19EWU5BTU9EQl9FTkRQT0lOVCBpcyBzZXQgd2hlbiB0aGUgY2xpZW50IGlzIGNyZWF0ZWQuXG4gKiBVc2FnZTogdHMtbm9kZSBpbXBvcnQtcGF0aWVudC13aXRoLWR5bmFsaXRlLnRzIDxwYXRoLXRvLXBhdGllbnQuanNvbj4gW3RlbmFudElkXSBbd29ya3NwYWNlSWRdXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIG1haW4oKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IFssICwgZmlsZUFyZywgdGVuYW50SWQgPSBcInRlbmFudC0xXCIsIHdvcmtzcGFjZUlkID0gXCJ3cy0xXCJdID1cbiAgICBwcm9jZXNzLmFyZ3Y7XG5cbiAgaWYgKCFmaWxlQXJnKSB7XG4gICAgY29uc29sZS5lcnJvcihcbiAgICAgIFwiVXNhZ2U6IGltcG9ydC1wYXRpZW50LXdpdGgtZHluYWxpdGUudHMgPHBhdGgtdG8tcGF0aWVudC5qc29uPiBbdGVuYW50SWRdIFt3b3Jrc3BhY2VJZF1cIixcbiAgICApO1xuICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgfVxuXG4gIC8vIFBvaW50IGplc3QtZHluYWxpdGUgYXQgdGhlIHBhY2thZ2Ugcm9vdCBzbyBpdCBmaW5kcyBqZXN0LWR5bmFsaXRlLWNvbmZpZy5cbiAgLy8gTXVzdCBydW4gYmVmb3JlIGFueSBpbXBvcnQgb2YgZWhyLXI0LWRhdGEtc2VydmljZSBzbyB0aGUgRHluYW1vREIgY2xpZW50IHNlZXMgTU9DS19EWU5BTU9EQl9FTkRQT0lOVC5cbiAgY29uc3QgcGFja2FnZVJvb3QgPSByZXNvbHZlKF9fZGlybmFtZSwgXCIuLi8uLlwiKTtcbiAgc2V0dXAocGFja2FnZVJvb3QpO1xuXG4gIGF3YWl0IHN0YXJ0RGIoKTtcbiAgYXdhaXQgY3JlYXRlVGFibGVzKCk7XG5cbiAgY29uc3QgeyBpbXBvcnRQYXRpZW50RnJvbUZpbGUgfSA9IGF3YWl0IGltcG9ydChcIi4vaW1wb3J0LXBhdGllbnRcIik7XG4gIGNvbnN0IHsgZ2V0RWhyUjREYXRhU2VydmljZSB9ID1cbiAgICBhd2FpdCBpbXBvcnQoXCIuL2R5bmFtby9laHIvcjQvZWhyLXI0LWRhdGEtc2VydmljZVwiKTtcblxuICBjb25zdCBzayA9IFwiQ1VSUkVOVFwiO1xuXG4gIHRyeSB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgaW1wb3J0UGF0aWVudEZyb21GaWxlKGZpbGVBcmcsIHtcbiAgICAgIHRlbmFudElkLFxuICAgICAgd29ya3NwYWNlSWQsXG4gICAgICB0YWJsZU5hbWU6IERBVEFfU1RPUkVfVEFCTEUsXG4gICAgfSk7XG4gICAgY29uc29sZS5sb2coXG4gICAgICBgSW1wb3J0ZWQgUGF0aWVudCAke3Jlc3VsdC5pZH0gKHRlbmFudD0ke3Jlc3VsdC50ZW5hbnRJZH0sIHdvcmtzcGFjZT0ke3Jlc3VsdC53b3Jrc3BhY2VJZH0pYCxcbiAgICApO1xuXG4gICAgY29uc3Qgc2VydmljZSA9IGdldEVoclI0RGF0YVNlcnZpY2UoREFUQV9TVE9SRV9UQUJMRSk7XG4gICAgY29uc3QgZ2V0UmVzdWx0ID0gYXdhaXQgc2VydmljZS5lbnRpdGllcy5wYXRpZW50XG4gICAgICAuZ2V0KHsgdGVuYW50SWQsIHdvcmtzcGFjZUlkLCBpZDogcmVzdWx0LmlkLCBzayB9KVxuICAgICAgLmdvKCk7XG5cbiAgICBpZiAoZ2V0UmVzdWx0LmRhdGEpIHtcbiAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICBcIlJlYWQgYmFjayBmcm9tIGR5bmFsaXRlOlwiLFxuICAgICAgICBKU09OLnN0cmluZ2lmeShnZXRSZXN1bHQuZGF0YSwgbnVsbCwgMiksXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zb2xlLmxvZyhcIlJlYWQgYmFjazogbm8gcmVjb3JkIGZvdW5kXCIpO1xuICAgIH1cbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgY29uc29sZS5lcnJvcihlcnIpO1xuICAgIHByb2Nlc3MuZXhpdENvZGUgPSAxO1xuICB9IGZpbmFsbHkge1xuICAgIGF3YWl0IHN0b3BEYigpO1xuICB9XG59XG5cbnZvaWQgbWFpbigpO1xuIl19
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Audit fields stored in FHIR resource meta.extension. */
|
|
2
|
+
export interface AuditFields {
|
|
3
|
+
createdDate?: string;
|
|
4
|
+
createdById?: string;
|
|
5
|
+
createdByName?: string;
|
|
6
|
+
modifiedDate?: string;
|
|
7
|
+
modifiedById?: string;
|
|
8
|
+
modifiedByName?: string;
|
|
9
|
+
deletedDate?: string;
|
|
10
|
+
deletedById?: string;
|
|
11
|
+
deletedByName?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Builds meta.extension entries for audit; merges with existing meta. */
|
|
14
|
+
export declare function mergeAuditIntoMeta(meta: Record<string, unknown> | undefined, audit: AuditFields): Record<string, unknown>;
|
|
15
|
+
/** Minimal FHIR Patient shape needed for import (id and resourceType required). */
|
|
16
|
+
interface FhirPatientLike {
|
|
17
|
+
resourceType: string;
|
|
18
|
+
id: string;
|
|
19
|
+
meta?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export interface ImportPatientOptions {
|
|
22
|
+
tenantId: string;
|
|
23
|
+
workspaceId: string;
|
|
24
|
+
tableName?: string;
|
|
25
|
+
/** Audit fields at same level as tenantId/workspaceId; merged with defaults. */
|
|
26
|
+
createdDate?: string;
|
|
27
|
+
createdById?: string;
|
|
28
|
+
createdByName?: string;
|
|
29
|
+
modifiedDate?: string;
|
|
30
|
+
modifiedById?: string;
|
|
31
|
+
modifiedByName?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Maps a FHIR Patient (from JSON) to the attributes required for Patient.put().
|
|
35
|
+
* Uses placeholder GSI2/GSI3 fields so ElectroDB index conditions are satisfied.
|
|
36
|
+
*/
|
|
37
|
+
export declare function patientToPutAttrs(patient: FhirPatientLike, options: ImportPatientOptions): Record<string, unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* Reads a single Patient JSON file and imports it into the FHIR store.
|
|
40
|
+
* Table name: options.tableName ?? process.env.DYNAMO_TABLE_NAME ?? "jesttesttable".
|
|
41
|
+
*/
|
|
42
|
+
export declare function importPatientFromFile(filePath: string, options: ImportPatientOptions): Promise<{
|
|
43
|
+
id: string;
|
|
44
|
+
tenantId: string;
|
|
45
|
+
workspaceId: string;
|
|
46
|
+
}>;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeAuditIntoMeta = mergeAuditIntoMeta;
|
|
4
|
+
exports.patientToPutAttrs = patientToPutAttrs;
|
|
5
|
+
exports.importPatientFromFile = importPatientFromFile;
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const compression_1 = require("../lib/compression");
|
|
9
|
+
const ehr_r4_data_service_1 = require("./dynamo/ehr/r4/ehr-r4-data-service");
|
|
10
|
+
/** OpenHI extension URLs for audit in resource meta (per ADR 2026-01-13-06). */
|
|
11
|
+
const OPENHI_EXT = "http://openhi.org/fhir/StructureDefinition";
|
|
12
|
+
/** Builds meta.extension entries for audit; merges with existing meta. */
|
|
13
|
+
function mergeAuditIntoMeta(meta, audit) {
|
|
14
|
+
const existing = meta ?? {};
|
|
15
|
+
const ext = [
|
|
16
|
+
...(Array.isArray(existing.extension)
|
|
17
|
+
? existing.extension
|
|
18
|
+
: []),
|
|
19
|
+
];
|
|
20
|
+
const byUrl = new Map(ext.map((e) => [e.url, e]));
|
|
21
|
+
function set(url, value, type) {
|
|
22
|
+
if (value == null)
|
|
23
|
+
return;
|
|
24
|
+
byUrl.set(url, { url, [type]: value });
|
|
25
|
+
}
|
|
26
|
+
set(`${OPENHI_EXT}/created-date`, audit.createdDate, "valueDateTime");
|
|
27
|
+
set(`${OPENHI_EXT}/created-by-id`, audit.createdById, "valueString");
|
|
28
|
+
set(`${OPENHI_EXT}/created-by-name`, audit.createdByName, "valueString");
|
|
29
|
+
set(`${OPENHI_EXT}/modified-date`, audit.modifiedDate, "valueDateTime");
|
|
30
|
+
set(`${OPENHI_EXT}/modified-by-id`, audit.modifiedById, "valueString");
|
|
31
|
+
set(`${OPENHI_EXT}/modified-by-name`, audit.modifiedByName, "valueString");
|
|
32
|
+
set(`${OPENHI_EXT}/deleted-date`, audit.deletedDate, "valueDateTime");
|
|
33
|
+
set(`${OPENHI_EXT}/deleted-by-id`, audit.deletedById, "valueString");
|
|
34
|
+
set(`${OPENHI_EXT}/deleted-by-name`, audit.deletedByName, "valueString");
|
|
35
|
+
return { ...existing, extension: Array.from(byUrl.values()) };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extracts a Patient from parsed JSON. Accepts either:
|
|
39
|
+
* - A standalone Patient resource (root has resourceType "Patient"), or
|
|
40
|
+
* - A FHIR Bundle (e.g. Synthea transaction) — uses the first entry whose resource is a Patient.
|
|
41
|
+
*/
|
|
42
|
+
function extractPatient(parsed) {
|
|
43
|
+
if (parsed && typeof parsed === "object" && "resourceType" in parsed) {
|
|
44
|
+
const root = parsed;
|
|
45
|
+
if (root.resourceType === "Patient" && root.id) {
|
|
46
|
+
return root;
|
|
47
|
+
}
|
|
48
|
+
if (root.resourceType === "Bundle" && "entry" in parsed) {
|
|
49
|
+
const entries = parsed.entry;
|
|
50
|
+
if (Array.isArray(entries)) {
|
|
51
|
+
const patientEntry = entries.find((e) => e?.resource?.resourceType === "Patient" && e.resource.id);
|
|
52
|
+
if (patientEntry?.resource) {
|
|
53
|
+
return patientEntry.resource;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
throw new Error("File must be a FHIR Patient resource or a Bundle containing at least one Patient entry");
|
|
59
|
+
}
|
|
60
|
+
const SK = "CURRENT";
|
|
61
|
+
/** Default audit values for create/modify when importing. */
|
|
62
|
+
const defaultAudit = {
|
|
63
|
+
createdDate: new Date().toISOString(),
|
|
64
|
+
createdById: "import",
|
|
65
|
+
createdByName: "Bulk import",
|
|
66
|
+
modifiedDate: new Date().toISOString(),
|
|
67
|
+
modifiedById: "import",
|
|
68
|
+
modifiedByName: "Bulk import",
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Maps a FHIR Patient (from JSON) to the attributes required for Patient.put().
|
|
72
|
+
* Uses placeholder GSI2/GSI3 fields so ElectroDB index conditions are satisfied.
|
|
73
|
+
*/
|
|
74
|
+
function patientToPutAttrs(patient, options) {
|
|
75
|
+
const { tenantId, workspaceId, createdDate, createdById, createdByName, modifiedDate, modifiedById, modifiedByName, } = options;
|
|
76
|
+
const lastUpdated = patient.meta?.lastUpdated ??
|
|
77
|
+
modifiedDate ??
|
|
78
|
+
defaultAudit.modifiedDate ??
|
|
79
|
+
new Date().toISOString();
|
|
80
|
+
const auditMerged = {
|
|
81
|
+
...defaultAudit,
|
|
82
|
+
...(createdDate != null && { createdDate }),
|
|
83
|
+
...(createdById != null && { createdById }),
|
|
84
|
+
...(createdByName != null && { createdByName }),
|
|
85
|
+
...(modifiedDate != null && { modifiedDate }),
|
|
86
|
+
...(modifiedById != null && { modifiedById }),
|
|
87
|
+
...(modifiedByName != null && { modifiedByName }),
|
|
88
|
+
};
|
|
89
|
+
const patientWithMeta = {
|
|
90
|
+
...patient,
|
|
91
|
+
meta: mergeAuditIntoMeta(patient.meta, auditMerged),
|
|
92
|
+
};
|
|
93
|
+
if (lastUpdated && !patientWithMeta.meta.lastUpdated) {
|
|
94
|
+
patientWithMeta.meta.lastUpdated = lastUpdated;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
sk: SK,
|
|
98
|
+
tenantId,
|
|
99
|
+
workspaceId,
|
|
100
|
+
id: patient.id,
|
|
101
|
+
resource: (0, compression_1.compressResource)(JSON.stringify(patientWithMeta)),
|
|
102
|
+
vid: lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) ||
|
|
103
|
+
Date.now().toString(36),
|
|
104
|
+
lastUpdated,
|
|
105
|
+
identifierSystem: "",
|
|
106
|
+
identifierValue: "",
|
|
107
|
+
facilityId: "",
|
|
108
|
+
normalizedName: "",
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Reads a single Patient JSON file and imports it into the FHIR store.
|
|
113
|
+
* Table name: options.tableName ?? process.env.DYNAMO_TABLE_NAME ?? "jesttesttable".
|
|
114
|
+
*/
|
|
115
|
+
async function importPatientFromFile(filePath, options) {
|
|
116
|
+
const resolved = (0, node_path_1.resolve)(filePath);
|
|
117
|
+
const raw = (0, node_fs_1.readFileSync)(resolved, "utf-8");
|
|
118
|
+
const parsed = JSON.parse(raw);
|
|
119
|
+
const patient = extractPatient(parsed);
|
|
120
|
+
const tableName = options.tableName ?? process.env.DYNAMO_TABLE_NAME ?? "jesttesttable";
|
|
121
|
+
const service = (0, ehr_r4_data_service_1.getEhrR4DataService)(tableName);
|
|
122
|
+
const attrs = patientToPutAttrs(patient, options);
|
|
123
|
+
const result = await service.entities.patient
|
|
124
|
+
.put(attrs)
|
|
125
|
+
.go();
|
|
126
|
+
const data = result.data;
|
|
127
|
+
if (!data) {
|
|
128
|
+
throw new Error(`Put failed for Patient ${patient.id}`);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
id: data.id,
|
|
132
|
+
tenantId: data.tenantId,
|
|
133
|
+
workspaceId: data.workspaceId,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** Run as script: node/ts-node import-patient.ts <path-to-patient.json> [tenantId] [workspaceId] */
|
|
137
|
+
async function main() {
|
|
138
|
+
const [, , fileArg, tenantId = "tenant-1", workspaceId = "ws-1"] = process.argv;
|
|
139
|
+
if (!fileArg) {
|
|
140
|
+
console.error("Usage: import-patient.ts <path-to-patient.json> [tenantId] [workspaceId]");
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const result = await importPatientFromFile(fileArg, {
|
|
145
|
+
tenantId,
|
|
146
|
+
workspaceId,
|
|
147
|
+
});
|
|
148
|
+
console.log(`Imported Patient ${result.id} (tenant=${result.tenantId}, workspace=${result.workspaceId})`);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.error(err);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (require.main === module) {
|
|
156
|
+
void main();
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1wb3J0LXBhdGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZGF0YS9pbXBvcnQtcGF0aWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQXNCQSxnREFxQ0M7QUFnRkQsOENBb0RDO0FBTUQsc0RBOEJDO0FBbk9ELHFDQUF1QztBQUN2Qyx5Q0FBb0M7QUFDcEMsb0RBQXNEO0FBQ3RELDZFQUEwRTtBQUUxRSxnRkFBZ0Y7QUFDaEYsTUFBTSxVQUFVLEdBQUcsNENBQTRDLENBQUM7QUFlaEUsMEVBQTBFO0FBQzFFLFNBQWdCLGtCQUFrQixDQUNoQyxJQUF5QyxFQUN6QyxLQUFrQjtJQUVsQixNQUFNLFFBQVEsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO0lBQzVCLE1BQU0sR0FBRyxHQUlKO1FBQ0gsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQztZQUNuQyxDQUFDLENBQUUsUUFBUSxDQUFDLFNBSVA7WUFDTCxDQUFDLENBQUMsRUFBRSxDQUFDO0tBQ1IsQ0FBQztJQUNGLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDbEQsU0FBUyxHQUFHLENBQ1YsR0FBVyxFQUNYLEtBQXlCLEVBQ3pCLElBQXFDO1FBRXJDLElBQUksS0FBSyxJQUFJLElBQUk7WUFBRSxPQUFPO1FBQzFCLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBQ0QsR0FBRyxDQUFDLEdBQUcsVUFBVSxlQUFlLEVBQUUsS0FBSyxDQUFDLFdBQVcsRUFBRSxlQUFlLENBQUMsQ0FBQztJQUN0RSxHQUFH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ZnNcIjtcbmltcG9ydCB7IHJlc29sdmUgfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBjb21wcmVzc1Jlc291cmNlIH0gZnJvbSBcIi4uL2xpYi9jb21wcmVzc2lvblwiO1xuaW1wb3J0IHsgZ2V0RWhyUjREYXRhU2VydmljZSB9IGZyb20gXCIuL2R5bmFtby9laHIvcjQvZWhyLXI0LWRhdGEtc2VydmljZVwiO1xuXG4vKiogT3BlbkhJIGV4dGVuc2lvbiBVUkxzIGZvciBhdWRpdCBpbiByZXNvdXJjZSBtZXRhIChwZXIgQURSIDIwMjYtMDEtMTMtMDYpLiAqL1xuY29uc3QgT1BFTkhJX0VYVCA9IFwiaHR0cDovL29wZW5oaS5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uXCI7XG5cbi8qKiBBdWRpdCBmaWVsZHMgc3RvcmVkIGluIEZISVIgcmVzb3VyY2UgbWV0YS5leHRlbnNpb24uICovXG5leHBvcnQgaW50ZXJmYWNlIEF1ZGl0RmllbGRzIHtcbiAgY3JlYXRlZERhdGU/OiBzdHJpbmc7XG4gIGNyZWF0ZWRCeUlkPzogc3RyaW5nO1xuICBjcmVhdGVkQnlOYW1lPzogc3RyaW5nO1xuICBtb2RpZmllZERhdGU/OiBzdHJpbmc7XG4gIG1vZGlmaWVkQnlJZD86IHN0cmluZztcbiAgbW9kaWZpZWRCeU5hbWU/OiBzdHJpbmc7XG4gIGRlbGV0ZWREYXRlPzogc3RyaW5nO1xuICBkZWxldGVkQnlJZD86IHN0cmluZztcbiAgZGVsZXRlZEJ5TmFtZT86IHN0cmluZztcbn1cblxuLyoqIEJ1aWxkcyBtZXRhLmV4dGVuc2lvbiBlbnRyaWVzIGZvciBhdWRpdDsgbWVyZ2VzIHdpdGggZXhpc3RpbmcgbWV0YS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtZXJnZUF1ZGl0SW50b01ldGEoXG4gIG1ldGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkLFxuICBhdWRpdDogQXVkaXRGaWVsZHMsXG4pOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIGNvbnN0IGV4aXN0aW5nID0gbWV0YSA/PyB7fTtcbiAgY29uc3QgZXh0OiBBcnJheTx7XG4gICAgdXJsOiBzdHJpbmc7XG4gICAgdmFsdWVTdHJpbmc/OiBzdHJpbmc7XG4gICAgdmFsdWVEYXRlVGltZT86IHN0cmluZztcbiAgfT4gPSBbXG4gICAgLi4uKEFycmF5LmlzQXJyYXkoZXhpc3RpbmcuZXh0ZW5zaW9uKVxuICAgICAgPyAoZXhpc3RpbmcuZXh0ZW5zaW9uIGFzIEFycmF5PHtcbiAgICAgICAgICB1cmw6IHN0cmluZztcbiAgICAgICAgICB2YWx1ZVN0cmluZz86IHN0cmluZztcbiAgICAgICAgICB2YWx1ZURhdGVUaW1lPzogc3RyaW5nO1xuICAgICAgICB9PilcbiAgICAgIDogW10pLFxuICBdO1xuICBjb25zdCBieVVybCA9IG5ldyBNYXAoZXh0Lm1hcCgoZSkgPT4gW2UudXJsLCBlXSkpO1xuICBmdW5jdGlvbiBzZXQoXG4gICAgdXJsOiBzdHJpbmcsXG4gICAgdmFsdWU6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICB0eXBlOiBcInZhbHVlU3RyaW5nXCIgfCBcInZhbHVlRGF0ZVRpbWVcIixcbiAgKSB7XG4gICAgaWYgKHZhbHVlID09IG51bGwpIHJldHVybjtcbiAgICBieVVybC5zZXQodXJsLCB7IHVybCwgW3R5cGVdOiB2YWx1ZSB9KTtcbiAgfVxuICBzZXQoYCR7T1BFTkhJX0VYVH0vY3JlYXRlZC1kYXRlYCwgYXVkaXQuY3JlYXRlZERhdGUsIFwidmFsdWVEYXRlVGltZVwiKTtcbiAgc2V0KGAke09QRU5ISV9FWFR9L2NyZWF0ZWQtYnktaWRgLCBhdWRpdC5jcmVhdGVkQnlJZCwgXCJ2YWx1ZVN0cmluZ1wiKTtcbiAgc2V0KGAke09QRU5ISV9FWFR9L2NyZWF0ZWQtYnktbmFtZWAsIGF1ZGl0LmNyZWF0ZWRCeU5hbWUsIFwidmFsdWVTdHJpbmdcIik7XG4gIHNldChgJHtPUEVOSElfRVhUfS9tb2RpZmllZC1kYXRlYCwgYXVkaXQubW9kaWZpZWREYXRlLCBcInZhbHVlRGF0ZVRpbWVcIik7XG4gIHNldChgJHtPUEVOSElfRVhUfS9tb2RpZmllZC1ieS1pZGAsIGF1ZGl0Lm1vZGlmaWVkQnlJZCwgXCJ2YWx1ZVN0cmluZ1wiKTtcbiAgc2V0KGAke09QRU5ISV9FWFR9L21vZGlmaWVkLWJ5LW5hbWVgLCBhdWRpdC5tb2RpZmllZEJ5TmFtZSwgXCJ2YWx1ZVN0cmluZ1wiKTtcbiAgc2V0KGAke09QRU5ISV9FWFR9L2RlbGV0ZWQtZGF0ZWAsIGF1ZGl0LmRlbGV0ZWREYXRlLCBcInZhbHVlRGF0ZVRpbWVcIik7XG4gIHNldChgJHtPUEVOSElfRVhUfS9kZWxldGVkLWJ5LWlkYCwgYXVkaXQuZGVsZXRlZEJ5SWQsIFwidmFsdWVTdHJpbmdcIik7XG4gIHNldChgJHtPUEVOSElfRVhUfS9kZWxldGVkLWJ5LW5hbWVgLCBhdWRpdC5kZWxldGVkQnlOYW1lLCBcInZhbHVlU3RyaW5nXCIpO1xuICByZXR1cm4geyAuLi5leGlzdGluZywgZXh0ZW5zaW9uOiBBcnJheS5mcm9tKGJ5VXJsLnZhbHVlcygpKSB9O1xufVxuXG4vKiogTWluaW1hbCBGSElSIFBhdGllbnQgc2hhcGUgbmVlZGVkIGZvciBpbXBvcnQgKGlkIGFuZCByZXNvdXJjZVR5cGUgcmVxdWlyZWQpLiAqL1xuaW50ZXJmYWNlIEZoaXJQYXRpZW50TGlrZSB7XG4gIHJlc291cmNlVHlwZTogc3RyaW5nO1xuICBpZDogc3RyaW5nO1xuICBtZXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG59XG5cbi8qKiBGSElSIEJ1bmRsZSBlbnRyeSAoZS5nLiBTeW50aGVhIHRyYW5zYWN0aW9uIGJ1bmRsZSkuICovXG5pbnRlcmZhY2UgQnVuZGxlRW50cnkge1xuICBmdWxsVXJsPzogc3RyaW5nO1xuICByZXNvdXJjZT86IHtcbiAgICByZXNvdXJjZVR5cGU/OiBzdHJpbmc7XG4gICAgaWQ/OiBzdHJpbmc7XG4gICAgbWV0YT86IHsgbGFzdFVwZGF0ZWQ/OiBzdHJpbmcgfTtcbiAgfTtcbn1cblxuLyoqXG4gKiBFeHRyYWN0cyBhIFBhdGllbnQgZnJvbSBwYXJzZWQgSlNPTi4gQWNjZXB0cyBlaXRoZXI6XG4gKiAtIEEgc3RhbmRhbG9uZSBQYXRpZW50IHJlc291cmNlIChyb290IGhhcyByZXNvdXJjZVR5cGUgXCJQYXRpZW50XCIpLCBvclxuICogLSBBIEZISVIgQnVuZGxlIChlLmcuIFN5bnRoZWEgdHJhbnNhY3Rpb24pIOKAlCB1c2VzIHRoZSBmaXJzdCBlbnRyeSB3aG9zZSByZXNvdXJjZSBpcyBhIFBhdGllbnQuXG4gKi9cbmZ1bmN0aW9uIGV4dHJhY3RQYXRpZW50KHBhcnNlZDogdW5rbm93bik6IEZoaXJQYXRpZW50TGlrZSB7XG4gIGlmIChwYXJzZWQgJiYgdHlwZW9mIHBhcnNlZCA9PT0gXCJvYmplY3RcIiAmJiBcInJlc291cmNlVHlwZVwiIGluIHBhcnNlZCkge1xuICAgIGNvbnN0IHJvb3QgPSBwYXJzZWQgYXMge1xuICAgICAgcmVzb3VyY2VUeXBlPzogc3RyaW5nO1xuICAgICAgaWQ/OiBzdHJpbmc7XG4gICAgICBtZXRhPzogeyBsYXN0VXBkYXRlZD86IHN0cmluZyB9O1xuICAgIH07XG4gICAgaWYgKHJvb3QucmVzb3VyY2VUeXBlID09PSBcIlBhdGllbnRcIiAmJiByb290LmlkKSB7XG4gICAgICByZXR1cm4gcm9vdCBhcyBGaGlyUGF0aWVudExpa2U7XG4gICAgfVxuICAgIGlmIChyb290LnJlc291cmNlVHlwZSA9PT0gXCJCdW5kbGVcIiAmJiBcImVudHJ5XCIgaW4gcGFyc2VkKSB7XG4gICAgICBjb25zdCBlbnRyaWVzID0gKHBhcnNlZCBhcyB7IGVudHJ5PzogQnVuZGxlRW50cnlbXSB9KS5lbnRyeTtcbiAgICAgIGlmIChBcnJheS5pc0FycmF5KGVudHJpZXMpKSB7XG4gICAgICAgIGNvbnN0IHBhdGllbnRFbnRyeSA9IGVudHJpZXMuZmluZChcbiAgICAgICAgICAoZSkgPT4gZT8ucmVzb3VyY2U/LnJlc291cmNlVHlwZSA9PT0gXCJQYXRpZW50XCIgJiYgZS5yZXNvdXJjZS5pZCxcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKHBhdGllbnRFbnRyeT8ucmVzb3VyY2UpIHtcbiAgICAgICAgICByZXR1cm4gcGF0aWVudEVudHJ5LnJlc291cmNlIGFzIEZoaXJQYXRpZW50TGlrZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgXCJGaWxlIG11c3QgYmUgYSBGSElSIFBhdGllbnQgcmVzb3VyY2Ugb3IgYSBCdW5kbGUgY29udGFpbmluZyBhdCBsZWFzdCBvbmUgUGF0aWVudCBlbnRyeVwiLFxuICApO1xufVxuXG5jb25zdCBTSyA9IFwiQ1VSUkVOVFwiO1xuXG4vKiogRGVmYXVsdCBhdWRpdCB2YWx1ZXMgZm9yIGNyZWF0ZS9tb2RpZnkgd2hlbiBpbXBvcnRpbmcuICovXG5jb25zdCBkZWZhdWx0QXVkaXQgPSB7XG4gIGNyZWF0ZWREYXRlOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gIGNyZWF0ZWRCeUlkOiBcImltcG9ydFwiLFxuICBjcmVhdGVkQnlOYW1lOiBcIkJ1bGsgaW1wb3J0XCIsXG4gIG1vZGlmaWVkRGF0ZTogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICBtb2RpZmllZEJ5SWQ6IFwiaW1wb3J0XCIsXG4gIG1vZGlmaWVkQnlOYW1lOiBcIkJ1bGsgaW1wb3J0XCIsXG59O1xuXG5leHBvcnQgaW50ZXJmYWNlIEltcG9ydFBhdGllbnRPcHRpb25zIHtcbiAgdGVuYW50SWQ6IHN0cmluZztcbiAgd29ya3NwYWNlSWQ6IHN0cmluZztcbiAgdGFibGVOYW1lPzogc3RyaW5nO1xuICAvKiogQXVkaXQgZmllbGRzIGF0IHNhbWUgbGV2ZWwgYXMgdGVuYW50SWQvd29ya3NwYWNlSWQ7IG1lcmdlZCB3aXRoIGRlZmF1bHRzLiAqL1xuICBjcmVhdGVkRGF0ZT86IHN0cmluZztcbiAgY3JlYXRlZEJ5SWQ/OiBzdHJpbmc7XG4gIGNyZWF0ZWRCeU5hbWU/OiBzdHJpbmc7XG4gIG1vZGlmaWVkRGF0ZT86IHN0cmluZztcbiAgbW9kaWZpZWRCeUlkPzogc3RyaW5nO1xuICBtb2RpZmllZEJ5TmFtZT86IHN0cmluZztcbn1cblxuLyoqXG4gKiBNYXBzIGEgRkhJUiBQYXRpZW50IChmcm9tIEpTT04pIHRvIHRoZSBhdHRyaWJ1dGVzIHJlcXVpcmVkIGZvciBQYXRpZW50LnB1dCgpLlxuICogVXNlcyBwbGFjZWhvbGRlciBHU0kyL0dTSTMgZmllbGRzIHNvIEVsZWN0cm9EQiBpbmRleCBjb25kaXRpb25zIGFyZSBzYXRpc2ZpZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXRpZW50VG9QdXRBdHRycyhcbiAgcGF0aWVudDogRmhpclBhdGllbnRMaWtlLFxuICBvcHRpb25zOiBJbXBvcnRQYXRpZW50T3B0aW9ucyxcbik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgY29uc3Qge1xuICAgIHRlbmFudElkLFxuICAgIHdvcmtzcGFjZUlkLFxuICAgIGNyZWF0ZWREYXRlLFxuICAgIGNyZWF0ZWRCeUlkLFxuICAgIGNyZWF0ZWRCeU5hbWUsXG4gICAgbW9kaWZpZWREYXRlLFxuICAgIG1vZGlmaWVkQnlJZCxcbiAgICBtb2RpZmllZEJ5TmFtZSxcbiAgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IGxhc3RVcGRhdGVkID1cbiAgICAocGF0aWVudC5tZXRhPy5sYXN0VXBkYXRlZCBhcyBzdHJpbmcgfCB1bmRlZmluZWQpID8/XG4gICAgbW9kaWZpZWREYXRlID8/XG4gICAgZGVmYXVsdEF1ZGl0Lm1vZGlmaWVkRGF0ZSA/P1xuICAgIG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKTtcbiAgY29uc3QgYXVkaXRNZXJnZWQ6IEF1ZGl0RmllbGRzID0ge1xuICAgIC4uLmRlZmF1bHRBdWRpdCxcbiAgICAuLi4oY3JlYXRlZERhdGUgIT0gbnVsbCAmJiB7IGNyZWF0ZWREYXRlIH0pLFxuICAgIC4uLihjcmVhdGVkQnlJZCAhPSBudWxsICYmIHsgY3JlYXRlZEJ5SWQgfSksXG4gICAgLi4uKGNyZWF0ZWRCeU5hbWUgIT0gbnVsbCAmJiB7IGNyZWF0ZWRCeU5hbWUgfSksXG4gICAgLi4uKG1vZGlmaWVkRGF0ZSAhPSBudWxsICYmIHsgbW9kaWZpZWREYXRlIH0pLFxuICAgIC4uLihtb2RpZmllZEJ5SWQgIT0gbnVsbCAmJiB7IG1vZGlmaWVkQnlJZCB9KSxcbiAgICAuLi4obW9kaWZpZWRCeU5hbWUgIT0gbnVsbCAmJiB7IG1vZGlmaWVkQnlOYW1lIH0pLFxuICB9O1xuXG4gIGNvbnN0IHBhdGllbnRXaXRoTWV0YSA9IHtcbiAgICAuLi5wYXRpZW50LFxuICAgIG1ldGE6IG1lcmdlQXVkaXRJbnRvTWV0YShwYXRpZW50Lm1ldGEsIGF1ZGl0TWVyZ2VkKSxcbiAgfTtcbiAgaWYgKGxhc3RVcGRhdGVkICYmICFwYXRpZW50V2l0aE1ldGEubWV0YS5sYXN0VXBkYXRlZCkge1xuICAgIChwYXRpZW50V2l0aE1ldGEubWV0YSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikubGFzdFVwZGF0ZWQgPSBsYXN0VXBkYXRlZDtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgc2s6IFNLLFxuICAgIHRlbmFudElkLFxuICAgIHdvcmtzcGFjZUlkLFxuICAgIGlkOiBwYXRpZW50LmlkLFxuICAgIHJlc291cmNlOiBjb21wcmVzc1Jlc291cmNlKEpTT04uc3RyaW5naWZ5KHBhdGllbnRXaXRoTWV0YSkpLFxuICAgIHZpZDpcbiAgICAgIGxhc3RVcGRhdGVkLnJlcGxhY2UoL1stOlQuWl0vZywgXCJcIikuc2xpY2UoMCwgMTIpIHx8XG4gICAgICBEYXRlLm5vdygpLnRvU3RyaW5nKDM2KSxcbiAgICBsYXN0VXBkYXRlZCxcbiAgICBpZGVudGlmaWVyU3lzdGVtOiBcIlwiLFxuICAgIGlkZW50aWZpZXJWYWx1ZTogXCJcIixcbiAgICBmYWNpbGl0eUlkOiBcIlwiLFxuICAgIG5vcm1hbGl6ZWROYW1lOiBcIlwiLFxuICB9O1xufVxuXG4vKipcbiAqIFJlYWRzIGEgc2luZ2xlIFBhdGllbnQgSlNPTiBmaWxlIGFuZCBpbXBvcnRzIGl0IGludG8gdGhlIEZISVIgc3RvcmUuXG4gKiBUYWJsZSBuYW1lOiBvcHRpb25zLnRhYmxlTmFtZSA/PyBwcm9jZXNzLmVudi5EWU5BTU9fVEFCTEVfTkFNRSA/PyBcImplc3R0ZXN0dGFibGVcIi5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGltcG9ydFBhdGllbnRGcm9tRmlsZShcbiAgZmlsZVBhdGg6IHN0cmluZyxcbiAgb3B0aW9uczogSW1wb3J0UGF0aWVudE9wdGlvbnMsXG4pOiBQcm9taXNlPHsgaWQ6IHN0cmluZzsgdGVuYW50SWQ6IHN0cmluZzsgd29ya3NwYWNlSWQ6IHN0cmluZyB9PiB7XG4gIGNvbnN0IHJlc29sdmVkID0gcmVzb2x2ZShmaWxlUGF0aCk7XG4gIGNvbnN0IHJhdyA9IHJlYWRGaWxlU3luYyhyZXNvbHZlZCwgXCJ1dGYtOFwiKTtcbiAgY29uc3QgcGFyc2VkOiB1bmtub3duID0gSlNPTi5wYXJzZShyYXcpO1xuICBjb25zdCBwYXRpZW50ID0gZXh0cmFjdFBhdGllbnQocGFyc2VkKTtcblxuICBjb25zdCB0YWJsZU5hbWUgPVxuICAgIG9wdGlvbnMudGFibGVOYW1lID8/IHByb2Nlc3MuZW52LkRZTkFNT19UQUJMRV9OQU1FID8/IFwiamVzdHRlc3R0YWJsZVwiO1xuICBjb25zdCBzZXJ2aWNlID0gZ2V0RWhyUjREYXRhU2VydmljZSh0YWJsZU5hbWUpO1xuICBjb25zdCBhdHRycyA9IHBhdGllbnRUb1B1dEF0dHJzKHBhdGllbnQsIG9wdGlvbnMpO1xuXG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNlcnZpY2UuZW50aXRpZXMucGF0aWVudFxuICAgIC5wdXQoYXR0cnMgYXMgdW5rbm93biBhcyBQYXJhbWV0ZXJzPHR5cGVvZiBzZXJ2aWNlLmVudGl0aWVzLnBhdGllbnQucHV0PlswXSlcbiAgICAuZ28oKTtcblxuICBjb25zdCBkYXRhID0gKFxuICAgIHJlc3VsdCBhcyB7IGRhdGE/OiB7IGlkOiBzdHJpbmc7IHRlbmFudElkOiBzdHJpbmc7IHdvcmtzcGFjZUlkOiBzdHJpbmcgfSB9XG4gICkuZGF0YTtcbiAgaWYgKCFkYXRhKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBQdXQgZmFpbGVkIGZvciBQYXRpZW50ICR7cGF0aWVudC5pZH1gKTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgaWQ6IGRhdGEuaWQsXG4gICAgdGVuYW50SWQ6IGRhdGEudGVuYW50SWQsXG4gICAgd29ya3NwYWNlSWQ6IGRhdGEud29ya3NwYWNlSWQsXG4gIH07XG59XG5cbi8qKiBSdW4gYXMgc2NyaXB0OiBub2RlL3RzLW5vZGUgaW1wb3J0LXBhdGllbnQudHMgPHBhdGgtdG8tcGF0aWVudC5qc29uPiBbdGVuYW50SWRdIFt3b3Jrc3BhY2VJZF0gKi9cbmFzeW5jIGZ1bmN0aW9uIG1haW4oKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IFssICwgZmlsZUFyZywgdGVuYW50SWQgPSBcInRlbmFudC0xXCIsIHdvcmtzcGFjZUlkID0gXCJ3cy0xXCJdID1cbiAgICBwcm9jZXNzLmFyZ3Y7XG5cbiAgaWYgKCFmaWxlQXJnKSB7XG4gICAgY29uc29sZS5lcnJvcihcbiAgICAgIFwiVXNhZ2U6IGltcG9ydC1wYXRpZW50LnRzIDxwYXRoLXRvLXBhdGllbnQuanNvbj4gW3RlbmFudElkXSBbd29ya3NwYWNlSWRdXCIsXG4gICAgKTtcbiAgICBwcm9jZXNzLmV4aXQoMSk7XG4gIH1cblxuICB0cnkge1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGltcG9ydFBhdGllbnRGcm9tRmlsZShmaWxlQXJnLCB7XG4gICAgICB0ZW5hbnRJZCxcbiAgICAgIHdvcmtzcGFjZUlkLFxuICAgIH0pO1xuICAgIGNvbnNvbGUubG9nKFxuICAgICAgYEltcG9ydGVkIFBhdGllbnQgJHtyZXN1bHQuaWR9ICh0ZW5hbnQ9JHtyZXN1bHQudGVuYW50SWR9LCB3b3Jrc3BhY2U9JHtyZXN1bHQud29ya3NwYWNlSWR9KWAsXG4gICAgKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgY29uc29sZS5lcnJvcihlcnIpO1xuICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgfVxufVxuXG5pZiAocmVxdWlyZS5tYWluID09PSBtb2R1bGUpIHtcbiAgdm9pZCBtYWluKCk7XG59XG4iXX0=
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
|
|
2
|
+
import { Construct } from "constructs";
|
|
3
|
+
export interface RestApiLambdaProps {
|
|
4
|
+
/**
|
|
5
|
+
* DynamoDB table name for the data store. The Lambda receives it as the
|
|
6
|
+
* environment variable DYNAMO_TABLE_NAME at runtime.
|
|
7
|
+
*/
|
|
8
|
+
readonly dynamoTableName: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class RestApiLambda extends Construct {
|
|
11
|
+
readonly lambda: NodejsFunction;
|
|
12
|
+
constructor(scope: Construct, props: RestApiLambdaProps);
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const handler: import("aws-lambda").Handler<any, any> & import("@codegenie/serverless-express/src/configure").ConfigureResult<any, any>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.handler = void 0;
|
|
7
|
+
const serverless_express_1 = __importDefault(require("@codegenie/serverless-express"));
|
|
8
|
+
const rest_api_1 = require("../rest-api/rest-api");
|
|
9
|
+
exports.handler = (0, serverless_express_1.default)({ app: rest_api_1.app });
|
|
10
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzdC1hcGktbGFtYmRhLmhhbmRsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZGF0YS9sYW1iZGEvcmVzdC1hcGktbGFtYmRhLmhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsdUZBQThEO0FBQzlELG1EQUEyQztBQUU5QixRQUFBLE9BQU8sR0FBRyxJQUFBLDRCQUFpQixFQUFDLEVBQUUsR0FBRyxFQUFILGNBQUcsRUFBRSxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgc2VydmVybGVzc0V4cHJlc3MgZnJvbSBcIkBjb2RlZ2VuaWUvc2VydmVybGVzcy1leHByZXNzXCI7XG5pbXBvcnQgeyBhcHAgfSBmcm9tIFwiLi4vcmVzdC1hcGkvcmVzdC1hcGlcIjtcblxuZXhwb3J0IGNvbnN0IGhhbmRsZXIgPSBzZXJ2ZXJsZXNzRXhwcmVzcyh7IGFwcCB9KTtcbiJdfQ==
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RestApiLambda = void 0;
|
|
4
|
+
const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
|
|
5
|
+
const aws_lambda_nodejs_1 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
6
|
+
const constructs_1 = require("constructs");
|
|
7
|
+
class RestApiLambda extends constructs_1.Construct {
|
|
8
|
+
constructor(scope, props) {
|
|
9
|
+
super(scope, "rest-api-lambda");
|
|
10
|
+
/**
|
|
11
|
+
* Create a Lambda function
|
|
12
|
+
*/
|
|
13
|
+
this.lambda = new aws_lambda_nodejs_1.NodejsFunction(this, "handler", {
|
|
14
|
+
runtime: aws_lambda_1.Runtime.NODEJS_LATEST,
|
|
15
|
+
environment: {
|
|
16
|
+
DYNAMO_TABLE_NAME: props.dynamoTableName,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.RestApiLambda = RestApiLambda;
|
|
22
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzdC1hcGktbGFtYmRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2RhdGEvbGFtYmRhL3Jlc3QtYXBpLWxhbWJkYS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx1REFBaUQ7QUFDakQscUVBQStEO0FBQy9ELDJDQUF1QztBQVV2QyxNQUFhLGFBQWMsU0FBUSxzQkFBUztJQUcxQyxZQUFZLEtBQWdCLEVBQUUsS0FBeUI7UUFDckQsS0FBSyxDQUFDLEtBQUssRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO1FBRWhDOztXQUVHO1FBQ0gsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLGtDQUFjLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRTtZQUNoRCxPQUFPLEVBQUUsb0JBQU8sQ0FBQyxhQUFhO1lBQzlCLFdBQVcsRUFBRTtnQkFDWCxpQkFBaUIsRUFBRSxLQUFLLENBQUMsZUFBZTthQUN6QztTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRjtBQWhCRCxzQ0FnQkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBSdW50aW1lIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGFcIjtcbmltcG9ydCB7IE5vZGVqc0Z1bmN0aW9uIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGEtbm9kZWpzXCI7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tIFwiY29uc3RydWN0c1wiO1xuXG5leHBvcnQgaW50ZXJmYWNlIFJlc3RBcGlMYW1iZGFQcm9wcyB7XG4gIC8qKlxuICAgKiBEeW5hbW9EQiB0YWJsZSBuYW1lIGZvciB0aGUgZGF0YSBzdG9yZS4gVGhlIExhbWJkYSByZWNlaXZlcyBpdCBhcyB0aGVcbiAgICogZW52aXJvbm1lbnQgdmFyaWFibGUgRFlOQU1PX1RBQkxFX05BTUUgYXQgcnVudGltZS5cbiAgICovXG4gIHJlYWRvbmx5IGR5bmFtb1RhYmxlTmFtZTogc3RyaW5nO1xufVxuXG5leHBvcnQgY2xhc3MgUmVzdEFwaUxhbWJkYSBleHRlbmRzIENvbnN0cnVjdCB7XG4gIHB1YmxpYyByZWFkb25seSBsYW1iZGE6IE5vZGVqc0Z1bmN0aW9uO1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIHByb3BzOiBSZXN0QXBpTGFtYmRhUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgXCJyZXN0LWFwaS1sYW1iZGFcIik7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYSBMYW1iZGEgZnVuY3Rpb25cbiAgICAgKi9cbiAgICB0aGlzLmxhbWJkYSA9IG5ldyBOb2RlanNGdW5jdGlvbih0aGlzLCBcImhhbmRsZXJcIiwge1xuICAgICAgcnVudGltZTogUnVudGltZS5OT0RFSlNfTEFURVNULFxuICAgICAgZW52aXJvbm1lbnQ6IHtcbiAgICAgICAgRFlOQU1PX1RBQkxFX05BTUU6IHByb3BzLmR5bmFtb1RhYmxlTmFtZSxcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Express middleware that sets req.openhiContext for /ehr (and other domain API)
|
|
4
|
+
* routes. Context includes tenantId, workspaceId, and the three standard audit
|
|
5
|
+
* fields (date, userId, username) for audit records and for merging into FHIR
|
|
6
|
+
* data on mutations.
|
|
7
|
+
*
|
|
8
|
+
* **TODO: A future task will populate context from JWT claims** (e.g. after
|
|
9
|
+
* verifying the token). For now, static values are used.
|
|
10
|
+
*
|
|
11
|
+
* @see sites/www-docs/content/packages/@openhi/constructs/rest-api.md — Middleware
|
|
12
|
+
*/
|
|
13
|
+
export declare function openHiContextMiddleware(req: Request, _res: Response, next: NextFunction): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.openHiContextMiddleware = openHiContextMiddleware;
|
|
4
|
+
/** Static context used until JWT claims are implemented. */
|
|
5
|
+
const STATIC_TENANT_ID = "tenant-1";
|
|
6
|
+
const STATIC_WORKSPACE_ID = "ws-1";
|
|
7
|
+
const STATIC_USER_ID = "rest-api";
|
|
8
|
+
const STATIC_USER_NAME = "REST API";
|
|
9
|
+
/**
|
|
10
|
+
* Express middleware that sets req.openhiContext for /ehr (and other domain API)
|
|
11
|
+
* routes. Context includes tenantId, workspaceId, and the three standard audit
|
|
12
|
+
* fields (date, userId, username) for audit records and for merging into FHIR
|
|
13
|
+
* data on mutations.
|
|
14
|
+
*
|
|
15
|
+
* **TODO: A future task will populate context from JWT claims** (e.g. after
|
|
16
|
+
* verifying the token). For now, static values are used.
|
|
17
|
+
*
|
|
18
|
+
* @see sites/www-docs/content/packages/@openhi/constructs/rest-api.md — Middleware
|
|
19
|
+
*/
|
|
20
|
+
function openHiContextMiddleware(req, _res, next) {
|
|
21
|
+
const now = new Date().toISOString();
|
|
22
|
+
req.openhiContext = {
|
|
23
|
+
tenantId: STATIC_TENANT_ID,
|
|
24
|
+
workspaceId: STATIC_WORKSPACE_ID,
|
|
25
|
+
date: now,
|
|
26
|
+
userId: STATIC_USER_ID,
|
|
27
|
+
username: STATIC_USER_NAME,
|
|
28
|
+
};
|
|
29
|
+
next();
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3Blbi1oaS1jb250ZXh0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2RhdGEvbWlkZGxld2FyZS9vcGVuLWhpLWNvbnRleHQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFtQkEsMERBY0M7QUEvQkQsNERBQTREO0FBQzVELE1BQU0sZ0JBQWdCLEdBQUcsVUFBVSxDQUFDO0FBQ3BDLE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxDQUFDO0FBQ25DLE1BQU0sY0FBYyxHQUFHLFVBQVUsQ0FBQztBQUNsQyxNQUFNLGdCQUFnQixHQUFHLFVBQVUsQ0FBQztBQUVwQzs7Ozs7Ozs7OztHQVVHO0FBQ0gsU0FBZ0IsdUJBQXVCLENBQ3JDLEdBQVksRUFDWixJQUFjLEVBQ2QsSUFBa0I7SUFFbEIsTUFBTSxHQUFHLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNyQyxHQUFHLENBQUMsYUFBYSxHQUFHO1FBQ2xCLFFBQVEsRUFBRSxnQkFBZ0I7UUFDMUIsV0FBVyxFQUFFLG1CQUFtQjtRQUNoQyxJQUFJLEVBQUUsR0FBRztRQUNULE1BQU0sRUFBRSxjQUFjO1FBQ3RCLFFBQVEsRUFBRSxnQkFBZ0I7S0FDM0IsQ0FBQztJQUNGLElBQUksRUFBRSxDQUFDO0FBQ1QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgUmVxdWVzdCwgUmVzcG9uc2UsIE5leHRGdW5jdGlvbiB9IGZyb20gXCJleHByZXNzXCI7XG5cbi8qKiBTdGF0aWMgY29udGV4dCB1c2VkIHVudGlsIEpXVCBjbGFpbXMgYXJlIGltcGxlbWVudGVkLiAqL1xuY29uc3QgU1RBVElDX1RFTkFOVF9JRCA9IFwidGVuYW50LTFcIjtcbmNvbnN0IFNUQVRJQ19XT1JLU1BBQ0VfSUQgPSBcIndzLTFcIjtcbmNvbnN0IFNUQVRJQ19VU0VSX0lEID0gXCJyZXN0LWFwaVwiO1xuY29uc3QgU1RBVElDX1VTRVJfTkFNRSA9IFwiUkVTVCBBUElcIjtcblxuLyoqXG4gKiBFeHByZXNzIG1pZGRsZXdhcmUgdGhhdCBzZXRzIHJlcS5vcGVuaGlDb250ZXh0IGZvciAvZWhyIChhbmQgb3RoZXIgZG9tYWluIEFQSSlcbiAqIHJvdXRlcy4gQ29udGV4dCBpbmNsdWRlcyB0ZW5hbnRJZCwgd29ya3NwYWNlSWQsIGFuZCB0aGUgdGhyZWUgc3RhbmRhcmQgYXVkaXRcbiAqIGZpZWxkcyAoZGF0ZSwgdXNlcklkLCB1c2VybmFtZSkgZm9yIGF1ZGl0IHJlY29yZHMgYW5kIGZvciBtZXJnaW5nIGludG8gRkhJUlxuICogZGF0YSBvbiBtdXRhdGlvbnMuXG4gKlxuICogKipUT0RPOiBBIGZ1dHVyZSB0YXNrIHdpbGwgcG9wdWxhdGUgY29udGV4dCBmcm9tIEpXVCBjbGFpbXMqKiAoZS5nLiBhZnRlclxuICogdmVyaWZ5aW5nIHRoZSB0b2tlbikuIEZvciBub3csIHN0YXRpYyB2YWx1ZXMgYXJlIHVzZWQuXG4gKlxuICogQHNlZSBzaXRlcy93d3ctZG9jcy9jb250ZW50L3BhY2thZ2VzL0BvcGVuaGkvY29uc3RydWN0cy9yZXN0LWFwaS5tZCDigJQgTWlkZGxld2FyZVxuICovXG5leHBvcnQgZnVuY3Rpb24gb3BlbkhpQ29udGV4dE1pZGRsZXdhcmUoXG4gIHJlcTogUmVxdWVzdCxcbiAgX3JlczogUmVzcG9uc2UsXG4gIG5leHQ6IE5leHRGdW5jdGlvbixcbik6IHZvaWQge1xuICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCk7XG4gIHJlcS5vcGVuaGlDb250ZXh0ID0ge1xuICAgIHRlbmFudElkOiBTVEFUSUNfVEVOQU5UX0lELFxuICAgIHdvcmtzcGFjZUlkOiBTVEFUSUNfV09SS1NQQUNFX0lELFxuICAgIGRhdGU6IG5vdyxcbiAgICB1c2VySWQ6IFNUQVRJQ19VU0VSX0lELFxuICAgIHVzZXJuYW1lOiBTVEFUSUNfVVNFUl9OQU1FLFxuICB9O1xuICBuZXh0KCk7XG59XG4iXX0=
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import express, { Request, Response } from "express";
|
|
2
|
+
declare const router: express.Router;
|
|
3
|
+
/**
|
|
4
|
+
* GET /ehr/r4/Patient — search/list: returns a FHIR Bundle (searchset) from the data store.
|
|
5
|
+
* Uses GSI4 (Resource Type Index) to list all Patients in the workspace without a table scan.
|
|
6
|
+
* @see {@link https://github.com/codedrifters/openhi/blob/main/sites/www-docs/content/architecture/dynamodb-single-table-design.md | DynamoDB Single-Table Design} — GSI4, Query and Access Rules (no scans).
|
|
7
|
+
*/
|
|
8
|
+
export declare function listPatients(req: Request, res: Response): Promise<Response>;
|
|
9
|
+
export declare function getPatientById(req: Request, res: Response): Promise<Response>;
|
|
10
|
+
/** POST /ehr/r4/Patient — create: accepts Patient in body, persists via data store, returns 201. */
|
|
11
|
+
export declare function createPatient(req: Request, res: Response): Promise<Response>;
|
|
12
|
+
/** PUT /ehr/r4/Patient/:id — update: accepts Patient in body, persists via data store, returns 200. */
|
|
13
|
+
export declare function updatePatient(req: Request, res: Response): Promise<Response>;
|
|
14
|
+
/** DELETE /ehr/r4/Patient/:id — delete: removes from data store, returns 204. */
|
|
15
|
+
export declare function deletePatient(req: Request, res: Response): Promise<Response>;
|
|
16
|
+
export { router as patientRouter };
|