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..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 @@ -4,11 +4,17 @@ 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"; import { IHEGatewayV2 } from "./ihe-gateway-v2"; +import dayjs from "dayjs"; +import duration from "dayjs/plugin/duration"; +dayjs.extend(duration); + +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; @@ -108,9 +114,13 @@ export class IHEGatewayV2Async extends IHEGatewayV2 { MAX_DOCUMENT_RETRIEVAL_REQUESTS_PER_INVOCATION ); - for (const chunk of requestChunks) { + for (const [i, chunk] of requestChunks.entries()) { const params = { patientId, cxId, requestId, drRequestsGatewayV2: chunk }; + if (i > 0) { + await sleep(SLEEP_IN_BETWEEN_DOCUMENT_RETRIEVAL_REQUESTS.asMilliseconds()); + } + // 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..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 @@ -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,12 @@ import { convertSoapResponseToMtomResponse, } from "../outbound/xca/mtom/parser"; +dayjs.extend(duration); + const { log } = out("Saml Client"); -const timeout = 120000; +const httpTimeout = dayjs.duration({ seconds: 120 }); +const initialDelay = dayjs.duration({ seconds: 3 }); +const maxPayloadSize = Infinity; let rejectUnauthorized = true; let trustedStore: string | undefined = undefined; async function getTrustedKeyStore(): Promise { @@ -84,7 +89,7 @@ export async function sendSignedXml({ const response = await executeWithNetworkRetries( async () => { return axios.post(url, signedXml, { - timeout: 120000, + timeout: httpTimeout.asMilliseconds(), headers: { "Content-Type": "application/soap+xml;charset=UTF-8", Accept: "application/soap+xml", @@ -94,7 +99,7 @@ export async function sendSignedXml({ }); }, { - initialDelay: 3000, + 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"], @@ -129,7 +134,7 @@ export async function sendSignedXmlMtom({ const response = await executeWithNetworkRetries( async () => { return axios.post(url, payload, { - timeout: timeout, + timeout: httpTimeout.asMilliseconds(), headers: { "Accept-Encoding": "gzip, deflate", "Content-Type": contentType, @@ -137,13 +142,21 @@ export async function sendSignedXmlMtom({ }, httpsAgent: agent, responseType: "arraybuffer", + maxBodyLength: maxPayloadSize, + maxContentLength: maxPayloadSize, }); }, { - initialDelay: 3000, - maxAttempts: 3, + 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: ["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..194bcc342d 100644 --- a/packages/shared/src/net/error.ts +++ b/packages/shared/src/net/error.ts @@ -3,6 +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 AxiosNetworkError = typeof AxiosError.ETIMEDOUT | typeof AxiosError.ECONNABORTED; +export type AxiosNetworkError = + | typeof AxiosError.ETIMEDOUT + | typeof AxiosError.ECONNABORTED + | typeof AxiosError.ERR_BAD_RESPONSE; export type NetworkError = NodeNetworkError | AxiosNetworkError;