Skip to content

Commit

Permalink
Merge pull request #2296 from metriport/develop
Browse files Browse the repository at this point in the history
RELEASE IHE V2 Durations Fix, Async Fix, Missing Dates Fix, Bump timeouts
  • Loading branch information
jonahkaye committed Jun 19, 2024
2 parents eb7e7c6 + c8e18aa commit 9b0256f
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export async function sendProcessRetryDqRequest({
cxId,
index,
});
return await processDqResponse({
return processDqResponse({
response,
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { buildIheResponseKey } from "../store";

it("should construct the correct file path and upload the file", async () => {
it("should construct the correct file path for type 'xcpd'", async () => {
const cxId = "cxId";
const patientId = "patientId";
const requestId = "requestId";
Expand All @@ -16,3 +16,22 @@ it("should construct the correct file path and upload the file", async () => {
});
expect(key).toEqual(`${cxId}/${patientId}/xcpd/${requestId}_2024-05-01/${oid}.xml`);
});

it("should construct the correct file path for type 'dr' with index", async () => {
const cxId = "cxId";
const patientId = "patientId";
const requestId = "requestId";
const oid = "oid";
const timestamp = "2024-05-01T00:00:00";
const index = 1;
const key = buildIheResponseKey({
type: "dr",
cxId,
patientId,
requestId,
oid,
timestamp,
index,
});
expect(key).toEqual(`${cxId}/${patientId}/dr/${requestId}_2024-05-01/${oid}_1.xml`);
});
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ export async function storeDrResponse({
response,
outboundRequest,
gateway,
index,
}: {
response: Buffer;
outboundRequest: OutboundDocumentRetrievalReq;
gateway: XCAGateway;
index: number;
}) {
try {
if (!bucket) {
Expand All @@ -112,6 +114,7 @@ export async function storeDrResponse({
requestId,
oid: gateway.homeCommunityId,
timestamp,
index,
});
await s3Utils.uploadFile({ bucket, key, file: response, contentType: "application/xml" });
} catch (error) {
Expand All @@ -126,14 +129,17 @@ export function buildIheResponseKey({
requestId,
oid,
timestamp,
index,
}: {
type: "xcpd" | "dq" | "dr";
cxId: string;
patientId: string;
requestId: string;
oid: string;
timestamp: string;
index?: number;
}) {
const date = dayjs(timestamp).format("YYYY-MM-DD");
return `${cxId}/${patientId}/${type}/${requestId}_${date}/${oid}.xml`;
const indexPart = index ? `_${index}` : "";
return `${cxId}/${patientId}/${type}/${requestId}_${date}/${oid}${indexPart}.xml`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,14 @@ describe("processDrResponse", () => {
expect(response.documentReference?.length).toBe(2);
expect(response?.documentReference?.[0]?.contentType).toEqual("application/octet-stream");
expect(response?.documentReference?.[0]?.docUniqueId).toEqual("123456789");
expect(response?.documentReference?.[0]?.homeCommunityId).toEqual("2.16.840.1.113883.3.8391");
expect(response?.documentReference?.[0]?.homeCommunityId).toEqual("2.16.840.1.113883.3.9621");
expect(response?.documentReference?.[0]?.repositoryUniqueId).toEqual(
"urn:oid:2.16.840.1.113883.3.9621"
);

expect(response?.documentReference?.[1]?.contentType).toEqual("application/octet-stream");
expect(response?.documentReference?.[1]?.docUniqueId).toEqual("987654321");
expect(response?.documentReference?.[1]?.homeCommunityId).toEqual("2.16.840.1.113883.3.8391");
expect(response?.documentReference?.[1]?.homeCommunityId).toEqual("2.16.840.1.113883.3.9621");
expect(response?.documentReference?.[1]?.repositoryUniqueId).toEqual(
"urn:oid:2.16.840.1.113883.3.9621"
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe("processXCPDResponse", () => {
expect(response).toEqual({
...expectedXcpdResponse,
responseTimestamp: expect.any(String),
requestTimestamp: expect.any(String),
});
});
it("should process the match XCPD response with multiple addresses and patient names correctly", async () => {
Expand All @@ -54,6 +55,7 @@ describe("processXCPDResponse", () => {
expect(response).toEqual({
...expectedMultiNameAddressResponse,
responseTimestamp: expect.any(String),
requestTimestamp: expect.any(String),
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
<ihe:RetrieveDocumentSetResponse xmlns:ihe="urn:ihe:iti:xds-b:2007">
<RegistryResponse xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rs:3.0" status="urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success"/>
<DocumentResponse>
<HomeCommunityId>urn:oid:urn:oid:2.16.840.1.113883.3.9621</HomeCommunityId>
<HomeCommunityId>urn:oid:2.16.840.1.113883.3.9621</HomeCommunityId>
<RepositoryUniqueId>urn:oid:2.16.840.1.113883.3.9621</RepositoryUniqueId>
<DocumentUniqueId>123456789</DocumentUniqueId>
<mimeType>application/pdf</mimeType>
<Document>Metriport Rocks!</Document>
</DocumentResponse>
<DocumentResponse>
<HomeCommunityId>urn:oid:urn:oid:2.16.840.1.113883.3.9621</HomeCommunityId>
<HomeCommunityId>urn:oid:2.16.840.1.113883.3.9621</HomeCommunityId>
<RepositoryUniqueId>urn:oid:2.16.840.1.113883.3.9621</RepositoryUniqueId>
<DocumentUniqueId>987654321</DocumentUniqueId>
<mimeType>application/xml</mimeType>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,17 @@ function getHomeCommunityIdForDr(extrinsicObject: ExtrinsicObject): string {
return getResponseHomeCommunityId(extrinsicObject);
}

function getCreationTime(time: string | undefined): string | undefined {
function getCreationTime({
creationTimeValue,
serviceStartTimeValue,
serviceStopTimeValue,
}: {
creationTimeValue: string | undefined;
serviceStartTimeValue: string | undefined;
serviceStopTimeValue: string | undefined;
}): string | undefined {
const time = creationTimeValue ?? serviceStartTimeValue ?? serviceStopTimeValue;

try {
return time ? dayjs.utc(time).toISOString() : undefined;
} catch (error) {
Expand Down Expand Up @@ -116,7 +126,8 @@ function parseDocumentReference({
}

const creationTimeValue = findSlotValue("creationTime");
const creationTime = creationTimeValue ? String(creationTimeValue) : undefined;
const serviceStartTimeValue = findSlotValue("serviceStartTime");
const serviceStopTimeValue = findSlotValue("serviceStopTime");

const documentReference: DocumentReference = {
homeCommunityId: getHomeCommunityIdForDr(extrinsicObject),
Expand All @@ -126,21 +137,21 @@ function parseDocumentReference({
language: findSlotValue("languageCode"),
size: sizeValue ? parseInt(sizeValue) : undefined,
title: findClassificationName(XDSDocumentEntryClassCode),
creation: getCreationTime(creationTime),
creation: getCreationTime({ creationTimeValue, serviceStartTimeValue, serviceStopTimeValue }),
authorInstitution: findClassificationSlotValue(XDSDocumentEntryAuthor, "authorInstitution"),
};
return documentReference;
}

async function handleSuccessResponse({
function handleSuccessResponse({
extrinsicObjects,
outboundRequest,
gateway,
}: {
extrinsicObjects: ExtrinsicObject[];
outboundRequest: OutboundDocumentQueryReq;
gateway: XCAGateway;
}): Promise<OutboundDocumentQueryResp> {
}): OutboundDocumentQueryResp {
const documentReferences = extrinsicObjects.flatMap(
extrinsicObject => parseDocumentReference({ extrinsicObject, outboundRequest }) ?? []
);
Expand All @@ -149,6 +160,7 @@ async function handleSuccessResponse({
id: outboundRequest.id,
patientId: outboundRequest.patientId,
timestamp: outboundRequest.timestamp,
requestTimestamp: outboundRequest.timestamp,
responseTimestamp: dayjs().toISOString(),
gateway,
documentReference: documentReferences,
Expand All @@ -162,7 +174,7 @@ export function processDqResponse({
response: { response, success, gateway, outboundRequest },
}: {
response: DQSamlClientResponse;
}): Promise<OutboundDocumentQueryResp> {
}): OutboundDocumentQueryResp {
if (success === false) {
return handleHttpErrorResponse({
httpError: response,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { getCidReference } from "../mtom/cid";
import { out } from "../../../../../../util/log";
import { errorToString, toArray } from "@metriport/shared";
import { iti39Schema, DocumentResponse } from "./schema";
import { capture } from "../../../../../../util/notifications";

const { log } = out("DR Processing");

Expand Down Expand Up @@ -88,52 +89,65 @@ async function processDocumentReference({
idMapping: Record<string, string>;
mtomResponse: MtomAttachments;
}): Promise<DocumentReference> {
const s3Utils = getS3UtilsInstance();
const { mimeType, decodedBytes } = getMtomBytesAndMimeType(documentResponse, mtomResponse);
const strippedDocUniqueId = stripUrnPrefix(documentResponse.DocumentUniqueId);
const metriportId = idMapping[strippedDocUniqueId];
if (!metriportId) {
throw new MetriportError("MetriportId not found for document");
}
try {
const s3Utils = getS3UtilsInstance();
const { mimeType, decodedBytes } = getMtomBytesAndMimeType(documentResponse, mtomResponse);
const strippedDocUniqueId = stripUrnPrefix(documentResponse.DocumentUniqueId);
const metriportId = idMapping[strippedDocUniqueId];
if (!metriportId) {
throw new MetriportError("MetriportId not found for document");
}

const filePath = createDocumentFilePath(
outboundRequest.cxId,
outboundRequest.patientId,
metriportId,
mimeType
);
const fileInfo = await s3Utils.getFileInfoFromS3(filePath, bucket);
const filePath = createDocumentFilePath(
outboundRequest.cxId,
outboundRequest.patientId,
metriportId,
mimeType
);
const fileInfo = await s3Utils.getFileInfoFromS3(filePath, bucket);

if (!fileInfo.exists) {
await s3Utils.uploadFile({
bucket,
key: filePath,
file: decodedBytes,
contentType: mimeType,
});
}

log(
`Downloaded a document with mime type: ${mimeType} for patient: ${outboundRequest.patientId} and request: ${outboundRequest.id}`
);

if (!fileInfo.exists) {
await s3Utils.uploadFile({
bucket,
key: filePath,
file: decodedBytes,
return {
url: s3Utils.buildFileUrl(bucket, filePath),
size: documentResponse.size ? parseInt(documentResponse.size) : undefined,
title: documentResponse.title,
fileName: filePath,
creation: documentResponse.creation,
language: documentResponse.language,
contentType: mimeType,
docUniqueId: documentResponse.DocumentUniqueId.toString(),
metriportId: metriportId,
fileLocation: bucket,
homeCommunityId: stripUrnPrefix(documentResponse.HomeCommunityId),
repositoryUniqueId: documentResponse.RepositoryUniqueId,
newDocumentUniqueId: documentResponse.NewDocumentUniqueId,
newRepositoryUniqueId: documentResponse.NewRepositoryUniqueId,
isNew: !fileInfo.exists,
};
} catch (error) {
const msg = "Error processing Document Reference";
log(`${msg}: ${error}`);
capture.error(msg, {
extra: {
error,
outboundRequest,
documentResponse,
},
});
throw new MetriportError(`Error Processing Document Reference`, error);
}

log(
`Downloaded a document with mime type: ${mimeType} for patient: ${outboundRequest.patientId} and request: ${outboundRequest.id}`
);

return {
url: s3Utils.buildFileUrl(bucket, filePath),
size: documentResponse.size ? parseInt(documentResponse.size) : undefined,
title: documentResponse.title,
fileName: filePath,
creation: documentResponse.creation,
language: documentResponse.language,
contentType: mimeType,
docUniqueId: documentResponse.DocumentUniqueId.toString(),
metriportId: metriportId,
fileLocation: bucket,
homeCommunityId: outboundRequest.gateway.homeCommunityId,
repositoryUniqueId: documentResponse.RepositoryUniqueId,
newDocumentUniqueId: documentResponse.NewDocumentUniqueId,
newRepositoryUniqueId: documentResponse.NewRepositoryUniqueId,
isNew: !fileInfo.exists,
};
}

function generateIdMapping(documentReferences: DocumentReference[]): Record<string, string> {
Expand All @@ -158,16 +172,24 @@ async function handleSuccessResponse({
}): Promise<OutboundDocumentRetrievalResp> {
try {
const idMapping = generateIdMapping(outboundRequest.documentReference);
const documentReferences = await Promise.all(
documentResponses.map(async (documentResponse: DocumentResponse) =>
const documentReferencesResults = await Promise.allSettled(
documentResponses.map((documentResponse: DocumentResponse) =>
processDocumentReference({ documentResponse, outboundRequest, idMapping, mtomResponse })
)
);

const documentReferences = documentReferencesResults
.filter(
(result): result is PromiseFulfilledResult<DocumentReference> =>
result.status === "fulfilled"
)
.map(result => result.value);

const response: OutboundDocumentRetrievalResp = {
id: outboundRequest.id,
patientId: outboundRequest.patientId,
timestamp: outboundRequest.timestamp,
requestTimestamp: outboundRequest.timestamp,
responseTimestamp: dayjs().toISOString(),
gateway,
documentReference: documentReferences,
Expand Down
Loading

0 comments on commit 9b0256f

Please sign in to comment.