diff --git a/containers/ecr-viewer/sql/core.sql b/containers/ecr-viewer/sql/core.sql index fce52e1962..c201bc35cc 100644 --- a/containers/ecr-viewer/sql/core.sql +++ b/containers/ecr-viewer/sql/core.sql @@ -2,6 +2,7 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE TABLE ecr_data ( eICR_ID VARCHAR(200) PRIMARY KEY, + set_id VARCHAR(255), data_source VARCHAR(2), -- S3 or DB fhir_reference_link VARCHAR(500), -- Link to the ecr fhir bundle patient_name_first VARCHAR(100), diff --git a/containers/ecr-viewer/src/app/api/save-fhir-data/save-fhir-data-service.ts b/containers/ecr-viewer/src/app/api/save-fhir-data/save-fhir-data-service.ts index dccfb61b72..241b99c03d 100644 --- a/containers/ecr-viewer/src/app/api/save-fhir-data/save-fhir-data-service.ts +++ b/containers/ecr-viewer/src/app/api/save-fhir-data/save-fhir-data-service.ts @@ -493,7 +493,7 @@ export const saveMetadataToPostgres = async ( await database.tx(async (t) => { // Insert main ECR metadata const saveToEcrData = new PQ({ - text: "INSERT INTO ecr_data (eICR_ID, patient_name_last, patient_name_first, patient_birth_date, data_source, report_date) VALUES ($1, $2, $3, $4, $5, $6)", + text: "INSERT INTO ecr_data (eICR_ID, patient_name_last, patient_name_first, patient_birth_date, data_source, report_date, set_id) VALUES ($1, $2, $3, $4, $5, $6, $7)", values: [ ecrId, metadata.last_name, @@ -501,6 +501,7 @@ export const saveMetadataToPostgres = async ( metadata.birth_date, "DB", metadata.report_date, + metadata.eicr_set_id, ], }); diff --git a/containers/ecr-viewer/src/app/api/save-fhir-data/types.ts b/containers/ecr-viewer/src/app/api/save-fhir-data/types.ts index fa4aafc5b7..c1b36cd6ee 100644 --- a/containers/ecr-viewer/src/app/api/save-fhir-data/types.ts +++ b/containers/ecr-viewer/src/app/api/save-fhir-data/types.ts @@ -84,6 +84,7 @@ export interface BundleMetadata { first_name: string; birth_date: string; data_source: string; + eicr_set_id: string | undefined; rr: RR[] | undefined; report_date: string; } diff --git a/containers/ecr-viewer/src/app/tests/save-fhir-data-service.test.tsx b/containers/ecr-viewer/src/app/tests/save-fhir-data-service.test.tsx new file mode 100644 index 0000000000..2614b8d00f --- /dev/null +++ b/containers/ecr-viewer/src/app/tests/save-fhir-data-service.test.tsx @@ -0,0 +1,173 @@ +/** + * @jest-environment node + */ + +import { saveMetadataToPostgres } from "../api/save-fhir-data/save-fhir-data-service"; +import { BundleMetadata } from "../api/save-fhir-data/types"; +import { getDB } from "../api/services/postgres_db"; + +jest.mock("../api/services/postgres_db", () => ({ + getDB: jest.fn(), +})); + +describe("saveMetadataToPostgres", () => { + const mockTransaction = { + query: jest.fn(), + none: jest.fn(), + one: jest.fn(), + }; + + const baseMetadata: BundleMetadata = { + last_name: "lname", + first_name: "fname", + birth_date: "01/01/2000", + data_source: "s3", + eicr_set_id: "1234", + rr: [], + report_date: "12/20/2024", + }; + + const saveEcrDataQuery = + "INSERT INTO ecr_data (eICR_ID, patient_name_last, patient_name_first, patient_birth_date, data_source, report_date, set_id) VALUES ($1, $2, $3, $4, $5, $6, $7)"; + const saveRRConditionsQuery = + "INSERT INTO ecr_rr_conditions (uuid, eICR_ID, condition) VALUES (uuid_generate_v4(), $1, $2) RETURNING uuid"; + const saveRRSummaryQuery = + "INSERT INTO ecr_rr_rule_summaries (uuid, ecr_rr_conditions_id, rule_summary) VALUES (uuid_generate_v4(), $1, $2)"; + beforeEach(() => { + const mockDatabase = { + tx: jest.fn(), + }; + + mockDatabase.tx.mockImplementation(async (callback) => { + return callback(mockTransaction); + }); + + (getDB as jest.Mock).mockReturnValue({ + database: mockDatabase, + pgPromise: { + ParameterizedQuery: jest + .fn() + .mockImplementation(({ text, values }: any) => ({ + text, + values, + })), + }, + }); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + it("should save without any rr", async () => { + const resp = await saveMetadataToPostgres(baseMetadata, "1-2-3-4"); + + expect(resp.message).toEqual("Success. Saved metadata to database."); + expect(resp.status).toEqual(200); + expect(mockTransaction.one).not.toHaveBeenCalled(); + expect(mockTransaction.none).toHaveBeenCalledExactlyOnceWith({ + text: saveEcrDataQuery, + values: [ + "1-2-3-4", + "lname", + "fname", + "01/01/2000", + "DB", + "12/20/2024", + "1234", + ], + }); + }); + + it("should save with rr without rule summaries", async () => { + const metadata: BundleMetadata = { + ...baseMetadata, + rr: [ + { + condition: "flu", + rule_summaries: [], + }, + ], + }; + + mockTransaction.one.mockReturnValue({ uuid: "return-id-1" }); + + const resp = await saveMetadataToPostgres(metadata, "1-2-3-4"); + + expect(resp.message).toEqual("Success. Saved metadata to database."); + expect(resp.status).toEqual(200); + expect(mockTransaction.one).toHaveBeenCalledExactlyOnceWith({ + text: saveRRConditionsQuery, + values: ["1-2-3-4", "flu"], + }); + expect(mockTransaction.none).toHaveBeenCalledExactlyOnceWith({ + text: saveEcrDataQuery, + values: [ + "1-2-3-4", + "lname", + "fname", + "01/01/2000", + "DB", + "12/20/2024", + "1234", + ], + }); + }); + + it("should save with rr with rule summaries", async () => { + const metadata: BundleMetadata = { + ...baseMetadata, + rr: [ + { + condition: "flu", + rule_summaries: [{ summary: "fever" }, { summary: "influenza" }], + }, + ], + }; + + mockTransaction.one.mockReturnValueOnce({ uuid: "return-id-1" }); + + const resp = await saveMetadataToPostgres(metadata, "1-2-3-4"); + + expect(resp.message).toEqual("Success. Saved metadata to database."); + expect(resp.status).toEqual(200); + expect(mockTransaction.one).toHaveBeenCalledExactlyOnceWith({ + text: saveRRConditionsQuery, + values: ["1-2-3-4", "flu"], + }); + expect(mockTransaction.none).toHaveBeenCalledTimes(3); + expect(mockTransaction.none).toHaveBeenNthCalledWith(1, { + text: saveEcrDataQuery, + values: [ + "1-2-3-4", + "lname", + "fname", + "01/01/2000", + "DB", + "12/20/2024", + "1234", + ], + }); + + expect(mockTransaction.none).toHaveBeenNthCalledWith(2, { + text: saveRRSummaryQuery, + values: ["return-id-1", "fever"], + }); + expect(mockTransaction.none).toHaveBeenNthCalledWith(3, { + text: saveRRSummaryQuery, + values: ["return-id-1", "influenza"], + }); + }); + + it("should return an error when db save fails", async () => { + jest.spyOn(console, "error").mockImplementation(() => {}); + + mockTransaction.none.mockRejectedValue({ error: "Connection timed out" }); + + const resp = await saveMetadataToPostgres(baseMetadata, "1-2-3-4"); + + expect(resp.message).toEqual("Failed to insert metadata to database."); + expect(resp.status).toEqual(500); + expect(mockTransaction.none).toHaveBeenCalledOnce(); + expect(mockTransaction.one).not.toHaveBeenCalled(); + }); +}); diff --git a/containers/message-parser/app/default_schemas/core.json b/containers/message-parser/app/default_schemas/core.json index 4d5fa96d62..54475d77c5 100644 --- a/containers/message-parser/app/default_schemas/core.json +++ b/containers/message-parser/app/default_schemas/core.json @@ -4,6 +4,11 @@ "data_type": "string", "nullable": false }, + "eicr_set_id": { + "fhir_path": "Bundle.entry.resource.where(resourceType= 'Composition').identifier.where(type.coding.code = '55751-2').where(use='official').value", + "data_type": "string", + "nullable": true + }, "last_name": { "fhir_path": "Bundle.entry.resource.where(resourceType = 'Patient').name.first().family", "data_type": "string",