From 2ac2825a02164a873e1c3eda9bbe505284b2a8e9 Mon Sep 17 00:00:00 2001 From: Jonah Kaye Date: Tue, 18 Jun 2024 16:27:31 -0400 Subject: [PATCH 1/5] fix(ihev2): increasing dr retries count + adding bad response retry Refs: #1667 Signed-off-by: Jonah Kaye --- .../ihe-gateway-v2/ihe-gateway-v2-async.ts | 8 +++++++- .../ihe-gateway-v2/outbound/xcpd/process/error.ts | 3 +++ .../carequality/ihe-gateway-v2/saml/saml-client.ts | 12 ++++++++++-- packages/shared/src/net/error.ts | 4 +++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts index 95250a969b..51db078620 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts @@ -4,6 +4,7 @@ import { OutboundDocumentQueryReq, OutboundDocumentRetrievalReq, } from "@metriport/ihe-gateway-sdk"; +import { sleep } from "@metriport/shared"; import { makeLambdaClient } from "../../aws/lambda"; import { Config } from "../../../util/config"; import { processAsyncError } from "../../../util/error/shared"; @@ -108,9 +109,14 @@ export class IHEGatewayV2Async extends IHEGatewayV2 { MAX_DOCUMENT_RETRIEVAL_REQUESTS_PER_INVOCATION ); - for (const chunk of requestChunks) { + for (let i = 0; i < requestChunks.length; i++) { + const chunk = requestChunks[i]; const params = { patientId, cxId, requestId, drRequestsGatewayV2: chunk }; + if (i > 0) { + await sleep(1000); + } + // intentionally not waiting lambdaClient .invoke({ diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/outbound/xcpd/process/error.ts b/packages/core/src/external/carequality/ihe-gateway-v2/outbound/xcpd/process/error.ts index ce9697e6f7..b9a80f8953 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/outbound/xcpd/process/error.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/outbound/xcpd/process/error.ts @@ -37,6 +37,7 @@ export function handleHttpErrorResponse({ gateway, patientId: outboundRequest?.patientId, patientMatch: null, + iheGatewayV2: true, operationOutcome, }; } @@ -74,6 +75,7 @@ export function handlePatientErrorResponse({ gateway, patientId: outboundRequest.patientId, patientMatch: null, + iheGatewayV2: true, operationOutcome, }; return response; @@ -108,6 +110,7 @@ export function handleSchemaErrorResponse({ gateway, patientId: outboundRequest.patientId, patientMatch: null, + iheGatewayV2: true, operationOutcome, }; return response; diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts b/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts index 28dcde7980..c36f2e58ee 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts @@ -137,13 +137,21 @@ export async function sendSignedXmlMtom({ }, httpsAgent: agent, responseType: "arraybuffer", + maxBodyLength: Infinity, + maxContentLength: Infinity, }); }, { initialDelay: 3000, - maxAttempts: 3, + maxAttempts: 4, //TODO: This introduces retry on timeout without needing to specify the http Code: https://github.com/metriport/metriport/pull/2285. Remove once PR is merged - httpCodesToRetry: ["ECONNREFUSED", "ECONNRESET", "ETIMEDOUT"], + httpCodesToRetry: [ + "ECONNREFUSED", + "ECONNRESET", + "ETIMEDOUT", + "ECONNABORTED", + "ERR_BAD_RESPONSE", + ], } ); diff --git a/packages/shared/src/net/error.ts b/packages/shared/src/net/error.ts index 0b9793b215..93cdcf3a0e 100644 --- a/packages/shared/src/net/error.ts +++ b/packages/shared/src/net/error.ts @@ -3,6 +3,8 @@ import { AxiosError } from "axios"; // https://nodejs.org/docs/latest-v18.x/api/errors.html#common-system-errors export type NodeNetworkError = "ECONNREFUSED" | "ECONNRESET"; +export type NodeResponseError = "ERR_BAD_RESPONSE"; + export type AxiosNetworkError = typeof AxiosError.ETIMEDOUT | typeof AxiosError.ECONNABORTED; -export type NetworkError = NodeNetworkError | AxiosNetworkError; +export type NetworkError = NodeNetworkError | AxiosNetworkError | NodeResponseError; From 335b7d5d1e53ea718b1c0b32ad0d39d213d47209 Mon Sep 17 00:00:00 2001 From: Jonah Kaye Date: Tue, 18 Jun 2024 17:38:48 -0400 Subject: [PATCH 2/5] fix(ihev2): constants Refs: #1667 Signed-off-by: Jonah Kaye --- .../carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts | 3 ++- .../carequality/ihe-gateway-v2/saml/saml-client.ts | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts index 51db078620..696a5d8e56 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts @@ -10,6 +10,7 @@ import { Config } from "../../../util/config"; import { processAsyncError } from "../../../util/error/shared"; import { IHEGatewayV2 } from "./ihe-gateway-v2"; +const SLEEP_IN_BETWEEN_DOCUMENT_RETRIEVAL_REQUESTS = 1000; const MAX_GATEWAYS_BEFORE_CHUNK = 1000; const MAX_DOCUMENT_QUERY_REQUESTS_PER_INVOCATION = 20; const MAX_DOCUMENT_RETRIEVAL_REQUESTS_PER_INVOCATION = 20; @@ -114,7 +115,7 @@ export class IHEGatewayV2Async extends IHEGatewayV2 { const params = { patientId, cxId, requestId, drRequestsGatewayV2: chunk }; if (i > 0) { - await sleep(1000); + await sleep(SLEEP_IN_BETWEEN_DOCUMENT_RETRIEVAL_REQUESTS); } // intentionally not waiting diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts b/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts index c36f2e58ee..115c6f2a42 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts @@ -18,6 +18,7 @@ import { const { log } = out("Saml Client"); const timeout = 120000; +const maxPayloadSize = Infinity; let rejectUnauthorized = true; let trustedStore: string | undefined = undefined; async function getTrustedKeyStore(): Promise { @@ -84,7 +85,7 @@ export async function sendSignedXml({ const response = await executeWithNetworkRetries( async () => { return axios.post(url, signedXml, { - timeout: 120000, + timeout: timeout, headers: { "Content-Type": "application/soap+xml;charset=UTF-8", Accept: "application/soap+xml", @@ -137,8 +138,8 @@ export async function sendSignedXmlMtom({ }, httpsAgent: agent, responseType: "arraybuffer", - maxBodyLength: Infinity, - maxContentLength: Infinity, + maxBodyLength: maxPayloadSize, + maxContentLength: maxPayloadSize, }); }, { From 3ea1c5da7b6152a4696557e845a37c98b0fdd1b0 Mon Sep 17 00:00:00 2001 From: Jonah Kaye Date: Tue, 18 Jun 2024 17:45:01 -0400 Subject: [PATCH 3/5] fix(ihev2): dayjs duration Refs: #1667 Signed-off-by: Jonah Kaye --- .../carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts index 696a5d8e56..34337c5b5e 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts @@ -9,8 +9,14 @@ import { makeLambdaClient } from "../../aws/lambda"; import { Config } from "../../../util/config"; import { processAsyncError } from "../../../util/error/shared"; import { IHEGatewayV2 } from "./ihe-gateway-v2"; +import dayjs from "dayjs"; +import duration from "dayjs/plugin/duration"; -const SLEEP_IN_BETWEEN_DOCUMENT_RETRIEVAL_REQUESTS = 1000; +dayjs.extend(duration); + +const SLEEP_IN_BETWEEN_DOCUMENT_RETRIEVAL_REQUESTS = dayjs + .duration({ seconds: 1 }) + .asMilliseconds(); const MAX_GATEWAYS_BEFORE_CHUNK = 1000; const MAX_DOCUMENT_QUERY_REQUESTS_PER_INVOCATION = 20; const MAX_DOCUMENT_RETRIEVAL_REQUESTS_PER_INVOCATION = 20; From f5a0e85a67ce5041c58901fedb051a11f27e89c9 Mon Sep 17 00:00:00 2001 From: Jonah Kaye Date: Tue, 18 Jun 2024 17:53:14 -0400 Subject: [PATCH 4/5] fix(ihev2): move error to axios, chunk instead of for Refs: #1667 Signed-off-by: Jonah Kaye --- .../ihe-gateway-v2/ihe-gateway-v2-async.ts | 3 +-- .../ihe-gateway-v2/saml/saml-client.ts | 16 ++++++++++------ packages/shared/src/net/error.ts | 9 +++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts index 34337c5b5e..e71a87ecc6 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts @@ -116,8 +116,7 @@ export class IHEGatewayV2Async extends IHEGatewayV2 { MAX_DOCUMENT_RETRIEVAL_REQUESTS_PER_INVOCATION ); - for (let i = 0; i < requestChunks.length; i++) { - const chunk = requestChunks[i]; + for (const [i, chunk] of requestChunks.entries()) { const params = { patientId, cxId, requestId, drRequestsGatewayV2: chunk }; if (i > 0) { diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts b/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts index 115c6f2a42..2a00646ef3 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts @@ -2,13 +2,14 @@ import https from "https"; import { constants } from "crypto"; import axios from "axios"; import * as AWS from "aws-sdk"; +import dayjs from "dayjs"; +import duration from "dayjs/plugin/duration"; import { SamlCertsAndKeys } from "./security/types"; import { Config } from "../../../../util/config"; import { out } from "../../../../util/log"; import { MetriportError } from "../../../../util/error/metriport-error"; import { createMtomContentTypeAndPayload } from "../outbound/xca/mtom/builder"; import { executeWithNetworkRetries } from "@metriport/shared"; - import { parseMtomResponse, getBoundaryFromMtomResponse, @@ -16,8 +17,11 @@ import { convertSoapResponseToMtomResponse, } from "../outbound/xca/mtom/parser"; +dayjs.extend(duration); + const { log } = out("Saml Client"); -const timeout = 120000; +const httpTimeout = dayjs.duration({ seconds: 120 }).asMilliseconds(); +const initialDelay = dayjs.duration({ seconds: 3 }).asMilliseconds(); const maxPayloadSize = Infinity; let rejectUnauthorized = true; let trustedStore: string | undefined = undefined; @@ -85,7 +89,7 @@ export async function sendSignedXml({ const response = await executeWithNetworkRetries( async () => { return axios.post(url, signedXml, { - timeout: timeout, + timeout: httpTimeout, headers: { "Content-Type": "application/soap+xml;charset=UTF-8", Accept: "application/soap+xml", @@ -95,7 +99,7 @@ export async function sendSignedXml({ }); }, { - initialDelay: 3000, + initialDelay: initialDelay, maxAttempts: 3, //TODO: This introduces retry on timeout without needing to specify the http Code: https://github.com/metriport/metriport/pull/2285. Remove once PR is merged httpCodesToRetry: ["ECONNREFUSED", "ECONNRESET", "ETIMEDOUT"], @@ -130,7 +134,7 @@ export async function sendSignedXmlMtom({ const response = await executeWithNetworkRetries( async () => { return axios.post(url, payload, { - timeout: timeout, + timeout: httpTimeout, headers: { "Accept-Encoding": "gzip, deflate", "Content-Type": contentType, @@ -143,7 +147,7 @@ export async function sendSignedXmlMtom({ }); }, { - initialDelay: 3000, + initialDelay: initialDelay, maxAttempts: 4, //TODO: This introduces retry on timeout without needing to specify the http Code: https://github.com/metriport/metriport/pull/2285. Remove once PR is merged httpCodesToRetry: [ diff --git a/packages/shared/src/net/error.ts b/packages/shared/src/net/error.ts index 93cdcf3a0e..194bcc342d 100644 --- a/packages/shared/src/net/error.ts +++ b/packages/shared/src/net/error.ts @@ -3,8 +3,9 @@ import { AxiosError } from "axios"; // https://nodejs.org/docs/latest-v18.x/api/errors.html#common-system-errors export type NodeNetworkError = "ECONNREFUSED" | "ECONNRESET"; -export type NodeResponseError = "ERR_BAD_RESPONSE"; +export type AxiosNetworkError = + | typeof AxiosError.ETIMEDOUT + | typeof AxiosError.ECONNABORTED + | typeof AxiosError.ERR_BAD_RESPONSE; -export type AxiosNetworkError = typeof AxiosError.ETIMEDOUT | typeof AxiosError.ECONNABORTED; - -export type NetworkError = NodeNetworkError | AxiosNetworkError | NodeResponseError; +export type NetworkError = NodeNetworkError | AxiosNetworkError; From 71430a592c9543319e746c75e5f19d7b37e9eb01 Mon Sep 17 00:00:00 2001 From: Jonah Kaye Date: Tue, 18 Jun 2024 18:09:36 -0400 Subject: [PATCH 5/5] fix(ihev2): ms in location its used Refs: #1667 Signed-off-by: Jonah Kaye --- .../ihe-gateway-v2/ihe-gateway-v2-async.ts | 6 ++---- .../carequality/ihe-gateway-v2/saml/saml-client.ts | 12 ++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts index e71a87ecc6..9509cdafa4 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/ihe-gateway-v2-async.ts @@ -14,9 +14,7 @@ import duration from "dayjs/plugin/duration"; dayjs.extend(duration); -const SLEEP_IN_BETWEEN_DOCUMENT_RETRIEVAL_REQUESTS = dayjs - .duration({ seconds: 1 }) - .asMilliseconds(); +const SLEEP_IN_BETWEEN_DOCUMENT_RETRIEVAL_REQUESTS = dayjs.duration({ seconds: 1 }); const MAX_GATEWAYS_BEFORE_CHUNK = 1000; const MAX_DOCUMENT_QUERY_REQUESTS_PER_INVOCATION = 20; const MAX_DOCUMENT_RETRIEVAL_REQUESTS_PER_INVOCATION = 20; @@ -120,7 +118,7 @@ export class IHEGatewayV2Async extends IHEGatewayV2 { const params = { patientId, cxId, requestId, drRequestsGatewayV2: chunk }; if (i > 0) { - await sleep(SLEEP_IN_BETWEEN_DOCUMENT_RETRIEVAL_REQUESTS); + await sleep(SLEEP_IN_BETWEEN_DOCUMENT_RETRIEVAL_REQUESTS.asMilliseconds()); } // intentionally not waiting diff --git a/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts b/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts index 2a00646ef3..051facf79a 100644 --- a/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts +++ b/packages/core/src/external/carequality/ihe-gateway-v2/saml/saml-client.ts @@ -20,8 +20,8 @@ import { dayjs.extend(duration); const { log } = out("Saml Client"); -const httpTimeout = dayjs.duration({ seconds: 120 }).asMilliseconds(); -const initialDelay = dayjs.duration({ seconds: 3 }).asMilliseconds(); +const httpTimeout = dayjs.duration({ seconds: 120 }); +const initialDelay = dayjs.duration({ seconds: 3 }); const maxPayloadSize = Infinity; let rejectUnauthorized = true; let trustedStore: string | undefined = undefined; @@ -89,7 +89,7 @@ export async function sendSignedXml({ const response = await executeWithNetworkRetries( async () => { return axios.post(url, signedXml, { - timeout: httpTimeout, + timeout: httpTimeout.asMilliseconds(), headers: { "Content-Type": "application/soap+xml;charset=UTF-8", Accept: "application/soap+xml", @@ -99,7 +99,7 @@ export async function sendSignedXml({ }); }, { - initialDelay: initialDelay, + initialDelay: initialDelay.asMilliseconds(), maxAttempts: 3, //TODO: This introduces retry on timeout without needing to specify the http Code: https://github.com/metriport/metriport/pull/2285. Remove once PR is merged httpCodesToRetry: ["ECONNREFUSED", "ECONNRESET", "ETIMEDOUT"], @@ -134,7 +134,7 @@ export async function sendSignedXmlMtom({ const response = await executeWithNetworkRetries( async () => { return axios.post(url, payload, { - timeout: httpTimeout, + timeout: httpTimeout.asMilliseconds(), headers: { "Accept-Encoding": "gzip, deflate", "Content-Type": contentType, @@ -147,7 +147,7 @@ export async function sendSignedXmlMtom({ }); }, { - initialDelay: initialDelay, + initialDelay: initialDelay.asMilliseconds(), maxAttempts: 4, //TODO: This introduces retry on timeout without needing to specify the http Code: https://github.com/metriport/metriport/pull/2285. Remove once PR is merged httpCodesToRetry: [