Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RELEASE 1801 1827 #2301

Merged
merged 22 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
034f2f2
fix(api): point API's getDocumentsFromFHIR to core's getDocuments
leite08 Jun 15, 2024
979f369
fix(api): point API's searchDocuments to core's searchDocuments
leite08 Jun 15, 2024
b5af7f7
refactor(core): return docRef w/ ID on search +
leite08 Jun 15, 2024
69d04c4
refactor(core): api to use Core's searchDocuments
leite08 Jun 15, 2024
c383685
build: update WH docs
leite08 Jun 15, 2024
def4f9c
fix(docs): typo on WH
leite08 Jun 17, 2024
4baa6ea
build: add wh types to start doc download, consolidated data and bulk…
leite08 Jun 18, 2024
4f7c54f
Merge pull request #2284 from metriport/1040-update-wh-docs
leite08 Jun 19, 2024
ce53590
Merge pull request #2282 from metriport/1827-doc-id
leite08 Jun 19, 2024
cfe5b26
refactor: use executeWithRetries and the net one throughout
leite08 Jun 15, 2024
ebcf17c
refactor: apply executeWithRetries to lower level funcitons +
leite08 Jun 18, 2024
556025f
refactor: apply executeWithRetries to missing lower level function
leite08 Jun 18, 2024
899a914
chore(release): publish
leite08 Jun 18, 2024
056427f
Merge pull request #2285 from metriport/1827-update-lambdas-to-retry
leite08 Jun 19, 2024
c72ffdf
fix(infra): 1/3 create ALB version of the API
leite08 Jun 19, 2024
5bd5ce6
fix(infra): 1/3 permissions to the ALB based fargate service
leite08 Jun 19, 2024
cfe3969
fix(infra): 1/3 add DB access to new ALB fargate service
leite08 Jun 19, 2024
3f316aa
fix(infra): 1/3 NLB pointing to ALB
leite08 Jun 19, 2024
e032d95
fix(infra): 1/3 use existing log on new ALB fargate
leite08 Jun 20, 2024
055dff2
Merge pull request #2303 from metriport/1040-alb-behind-api-nlb_1
leite08 Jun 20, 2024
dfddbbe
chore(release): publish
leite08 Jun 20, 2024
3aca99d
Merge pull request #2308 from metriport/1827-publish-npm-packages
leite08 Jun 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor: use executeWithRetries and the net one throughout
Ref. metriport/metriport-internal#1827

Signed-off-by: Rafael Leite <[email protected]>
  • Loading branch information
leite08 committed Jun 19, 2024
commit cfe5b26e70b0f4fb7923e2e04a27f963629e8c38
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/api-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"@medplum/fhirtypes": "^2.0.32",
"@metriport/commonwell-sdk": "^4.15.14",
"@metriport/shared": "^0.9.7",
"axios": "^1.3.4",
"axios": "^1.4.0",
"dayjs": "^1.11.7",
"dotenv": "^16.3.1",
"zod": "^3.22.1"
Expand Down
22 changes: 15 additions & 7 deletions packages/api/src/external/commonwell/patient-external-data.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Patient } from "@metriport/core/domain/patient";
import { DiscoveryParams } from "@metriport/core/domain/patient-discovery";
import { MetriportError } from "@metriport/core/util/error/metriport-error";
import { executeWithRetriesSafe } from "@metriport/shared";
import { executeWithRetriesSafe, MetriportError } from "@metriport/shared";
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import { getPatientOrFail } from "../../command/medical/patient/get-patient";
Expand All @@ -14,7 +13,7 @@ import { CQLinkStatus, PatientDataCommonwell } from "./patient-shared";
dayjs.extend(duration);

const maxAttemptsToGetPatientCWData = 5;
const waitTimeBetweenAttemptsToGetPatientCWData = dayjs.duration(2, "seconds");
const waitTimeBetweenAttemptsToGetPatientCWData = dayjs.duration(1, "seconds");

export type PatientWithCWData = Patient & {
data: { externalData: { COMMONWELL: PatientDataCommonwell } };
Expand All @@ -23,8 +22,8 @@ export type PatientWithCWData = Patient & {
const _getPatientWithCWData = async ({
id,
cxId,
}: Pick<Patient, "id" | "cxId">): Promise<PatientWithCWData | undefined> => {
const patientDB: Patient = await getPatientOrFail({
}: Pick<Patient, "id" | "cxId">): Promise<PatientWithCWData> => {
const patientDB = await getPatientOrFail({
id,
cxId,
});
Expand All @@ -33,7 +32,17 @@ const _getPatientWithCWData = async ({
if (!cwData) throw new MetriportError(`Missing CW data on patient`);
if (!cwData.patientId) throw new MetriportError(`Missing CW patientId`);

return patientDB as PatientWithCWData;
const patient = patientDB.dataValues;
return {
...patient,
data: {
...patient.data,
externalData: {
...patient.data.externalData,
COMMONWELL: cwData,
},
},
};
};

export async function getPatientWithCWData(
Expand All @@ -42,7 +51,6 @@ export async function getPatientWithCWData(
return executeWithRetriesSafe(() => _getPatientWithCWData(patient), {
maxAttempts: maxAttemptsToGetPatientCWData,
initialDelay: waitTimeBetweenAttemptsToGetPatientCWData.asMilliseconds(),
backoffMultiplier: 0, // no backoff
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Bundle, DocumentReference } from "@medplum/fhirtypes";
import { out } from "@metriport/core/util/log";
import { executeWithRetries } from "@metriport/shared";
import { executeWithNetworkRetries } from "@metriport/shared";
import { errorToString } from "../../../shared/log";
import { makeFhirApi } from "../api/api-factory";

Expand All @@ -14,10 +14,9 @@ export const upsertDocumentToFHIRServer = async (
): Promise<void> => {
const fhir = makeFhirApi(cxId);
try {
await executeWithRetries(async () => await fhir.updateResource(docRef), {
await executeWithNetworkRetries(async () => await fhir.updateResource(docRef), {
maxAttempts,
initialDelay: waitTimeBetweenAttemptsInMillis,
backoffMultiplier: 0, // no backoff
log,
});
} catch (err) {
Expand All @@ -33,10 +32,9 @@ export const upsertDocumentsToFHIRServer = async (
): Promise<void> => {
const fhir = makeFhirApi(cxId);
try {
await executeWithRetries(async () => await fhir.executeBatch(transactionBundle), {
await executeWithNetworkRetries(async () => await fhir.executeBatch(transactionBundle), {
maxAttempts,
initialDelay: waitTimeBetweenAttemptsInMillis,
backoffMultiplier: 0, // no backoff
log,
});
} catch (error) {
Expand Down
10 changes: 3 additions & 7 deletions packages/api/src/routes/medical/internal-docs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { BulkGetDocUrlStatus } from "@metriport/core/domain/bulk-get-document-url";
import {
DocumentBulkSignerLambdaResponse,
documentBulkSignerLambdaResponseArraySchema,
} from "@metriport/core/external/aws/document-signing/document-bulk-signer-response";
import { convertResult } from "@metriport/core/domain/document-query";
import { createDocumentFilePath } from "@metriport/core/domain/document/filename";
import { documentBulkSignerLambdaResponseArraySchema } from "@metriport/core/external/aws/document-signing/document-bulk-signer-response";
import { S3Utils } from "@metriport/core/external/aws/s3";
import { isMedicalDataSource } from "@metriport/core/external/index";
import { uuidv7 } from "@metriport/core/util/uuid-v7";
Expand Down Expand Up @@ -387,8 +384,7 @@ router.post(
const patientId = getFrom("query").orFail("patientId", req);
const requestId = getFrom("query").orFail("requestId", req);
const status = getFrom("query").orFail("status", req);
const dtos: DocumentBulkSignerLambdaResponse[] =
documentBulkSignerLambdaResponseArraySchema.parse(req.body);
const docs = documentBulkSignerLambdaResponseArraySchema.parse(req.body);

const updatedPatient = await appendBulkGetDocUrlProgress({
patient: { id: patientId, cxId },
Expand All @@ -403,7 +399,7 @@ router.post(
"medical.document-bulk-download-urls",
status as MAPIWebhookStatus,
requestId,
dtos
docs
);

return res.status(httpStatus.OK).json(updatedPatient.data.bulkGetDocumentsUrlProgress);
Expand Down
2 changes: 1 addition & 1 deletion packages/commonwell-cert-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
},
"dependencies": {
"@metriport/commonwell-sdk": "^4.15.14",
"axios": "^1.3.5",
"axios": "^1.4.0",
"commander": "^9.5.0",
"dayjs": "^1.11.7",
"dotenv": "^16.0.3",
Expand Down
3 changes: 2 additions & 1 deletion packages/commonwell-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
"url": "https://github.com/metriport/metriport/issues"
},
"dependencies": {
"axios": "^1.3.5",
"@metriport/shared": "^0.9.6",
"axios": "^1.4.0",
"http-status": "^1.7.0",
"jsonwebtoken": "^9.0.0",
"zod": "^3.22.1"
Expand Down
14 changes: 8 additions & 6 deletions packages/commonwell-sdk/src/common/commonwell-error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import axios from "axios";
import { getNetworkErrorDetails } from "@metriport/shared";

class Response {
status: number | undefined;
Expand All @@ -13,6 +13,7 @@ export type AdditionalInfo = {

export class CommonwellError extends Error {
private _status: number | undefined;
private _code: string | undefined;
public readonly cwReference: string | undefined;

constructor(
Expand All @@ -22,18 +23,19 @@ export class CommonwellError extends Error {
) {
super(message);
if (cause) {
if (axios.isAxiosError(cause)) {
this._status = cause.response?.status;
} else {
this._status = cause.status;
}
const { code, status } = getNetworkErrorDetails(cause);
this._status = status;
this._code = code;
}
this.cwReference = additionalInfo?.cwReference;
}

get status(): number | undefined {
return this._status;
}
get code(): string | undefined {
return this._code;
}
get response(): Response {
return {
status: this._status,
Expand Down
77 changes: 45 additions & 32 deletions packages/core/src/external/aws/document-signing/bulk-sign.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import {
executeWithNetworkRetries,
getNetworkErrorDetails,
MetriportError,
} from "@metriport/shared";
import axios from "axios";
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import { out } from "../../../util/log";
import { capture } from "../../../util/notifications";
import { searchDocuments } from "../../opensearch/search-documents";
import { S3Utils } from "../s3";
Expand All @@ -26,6 +32,7 @@ export async function searchDocumentsSignUrlsAndSendToApi(
apiURL: string
) {
const s3Utils = new S3Utils(region);
const { log } = out("getSignedUrls");

const documents = await searchDocuments({ cxId, patientId });
const ossApiClient = apiClientBulkDownloadWebhook(apiURL);
Expand All @@ -39,6 +46,10 @@ export async function searchDocumentsSignUrlsAndSendToApi(

const fileName = attachment?.title;
if (!fileName) {
const msg = `Found document without attachment title (filename)`;
const extra = { cxId, patientId, requestId, doc };
log(`${msg} - ${JSON.stringify(extra)}`);
capture.message(msg, { extra, level: "warning" });
return;
}

Expand All @@ -64,23 +75,27 @@ export async function searchDocumentsSignUrlsAndSendToApi(

const response = urls.flatMap(url => (url !== undefined ? url : []));

await ossApiClient.callInternalEndpoint({
cxId: cxId,
patientId: patientId,
requestId: requestId,
dtos: response,
status: "completed",
});
await executeWithNetworkRetries(() =>
ossApiClient.callInternalEndpoint({
cxId,
patientId,
requestId,
docs: response,
status: "completed",
})
);
} catch (error) {
const msg = "Error getting signed URLs";
const extra = { ...getNetworkErrorDetails(error), cxId, patientId, requestId };
log(`${msg} - ${JSON.stringify(extra)}`);
capture.error(msg, {
extra: { patientId, context: `getSignedUrls`, error },
extra: { ...extra, context: `getSignedUrls`, error },
});
await ossApiClient.callInternalEndpoint({
cxId: cxId,
patientId: patientId,
requestId: requestId,
dtos: [],
cxId,
patientId,
requestId,
docs: [],
status: "failed",
});
}
Expand All @@ -90,35 +105,33 @@ export type BulkDownloadWebhookParams = {
cxId: string;
patientId: string;
requestId: string;
dtos: DocumentBulkSignerLambdaResponse[];
docs: DocumentBulkSignerLambdaResponse[];
status: string;
};

export function apiClientBulkDownloadWebhook(apiURL: string) {
const { log } = out("apiClientBulkDownloadWebhook");
const sendBulkDownloadUrl = `${apiURL}/internal/docs/triggerBulkDownloadWebhook`;

return {
callInternalEndpoint: async function (params: BulkDownloadWebhookParams) {
try {
await ossApi.post(sendBulkDownloadUrl, params.dtos, {
params: {
cxId: params.cxId,
patientId: params.patientId,
requestId: params.requestId,
status: params.status,
},
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
const msg = "Error notifying API";
const extra = {
url: sendBulkDownloadUrl,
statusCode: error.response?.status,
error,
};
console.log(msg, extra);
capture.message(msg, { extra, level: "info" });
throw new Error(`Error from API: ${error.message}`);
await executeWithNetworkRetries(() =>
ossApi.post(sendBulkDownloadUrl, params.docs, {
params: {
cxId: params.cxId,
patientId: params.patientId,
requestId: params.requestId,
status: params.status,
},
})
);
} catch (error) {
const msg = "Error notifying API on bulk-sign";
const extra = { ...getNetworkErrorDetails(error), url: sendBulkDownloadUrl };
log(`${msg} - ${JSON.stringify(extra)}`);
capture.message(msg, { extra: { ...extra, params, error }, level: "info" });
throw new MetriportError(msg, error, extra);
}
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DocumentReference } from "@medplum/fhirtypes";
import { errorToString } from "@metriport/shared";
import { errorToString, executeWithNetworkRetries, executeWithRetries } from "@metriport/shared";
import axios from "axios";
import { createDocumentFileName } from "../../../domain/document/filename";
import {
Expand Down Expand Up @@ -55,7 +55,10 @@ export async function documentUploaderHandler(

// Make a copy of the file to the general medical documents bucket
try {
await s3Utils.s3.copyObject(params).promise();
await executeWithRetries(() => s3Utils.s3.copyObject(params).promise(), {
maxAttempts: 3,
initialDelay: 500,
});
log(`Successfully copied the uploaded file to ${destinationBucket} with key ${destinationKey}`);
} catch (error) {
const message = "Error copying the uploaded file to medical documents bucket";
Expand Down Expand Up @@ -121,7 +124,7 @@ async function forwardCallToServer(
const url = `${apiServerURL}?cxId=${cxId}`;
const encodedUrl = encodeURI(url);

const resp = await api.post(encodedUrl, fileData);
const resp = await executeWithNetworkRetries(() => api.post(encodedUrl, fileData));
log(`Server response - status: ${resp.status}`);
log(`Server response - body: ${JSON.stringify(resp.data)}`);
return resp.data;
Expand Down
Loading