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 5 commits
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
70 changes: 8 additions & 62 deletions packages/api/src/external/fhir/document/get-documents.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,11 @@
import { DocumentReference } from "@medplum/fhirtypes";
import { chunk } from "lodash";
import { capture } from "../../../shared/notifications";
import { makeFhirApi } from "../api/api-factory";
import {
isoDateToFHIRDateQueryFrom,
isoDateToFHIRDateQueryTo,
} from "@metriport/core/external/fhir/shared/index";
import { getDocuments } from "@metriport/core/external/fhir/document/get-documents";

export async function getDocumentsFromFHIR({
cxId,
patientId,
from,
to,
documentIds,
}: {
cxId: string;
patientId?: string | string[];
from?: string;
to?: string;
documentIds?: string[];
}): Promise<DocumentReference[]> {
try {
const api = makeFhirApi(cxId);
const docs: DocumentReference[] = [];
const chunksDocIds = documentIds && documentIds.length > 0 ? chunk(documentIds, 150) : [[]];

for (const docIds of chunksDocIds) {
const filtersAsStr = getFilters({ patientId, documentIds: docIds, from, to });
for await (const page of api.searchResourcePages("DocumentReference", filtersAsStr)) {
docs.push(...page);
}
}
return docs;
} catch (error) {
const msg = `Error getting documents from FHIR server`;
console.log(`${msg} - patientId: ${patientId}, error: ${error}`);
capture.message(msg, { extra: { patientId, error }, level: "error" });
throw error;
}
}

export function getFilters({
patientId: patientIdParam,
documentIds = [],
from,
to,
}: {
patientId?: string | string[];
documentIds?: string[];
from?: string;
to?: string;
} = {}) {
const filters = new URLSearchParams();
const patientIds = Array.isArray(patientIdParam) ? patientIdParam : [patientIdParam];
const patientIdsFiltered = patientIds.flatMap(id =>
id && id.trim().length > 0 ? id.trim() : []
);
patientIdsFiltered.length && filters.append("patient", patientIdsFiltered.join(","));
documentIds.length && filters.append(`_id`, documentIds.join(","));
from && filters.append("date", isoDateToFHIRDateQueryFrom(from));
to && filters.append("date", isoDateToFHIRDateQueryTo(to));
const filtersAsStr = filters.toString();
return filtersAsStr;
/**
* @deprecated Use `getDocuments()` from `@metriport/core/external/fhir/document/get-documents` instead.
*/
export async function getDocumentsFromFHIR(
...params: Parameters<typeof getDocuments>
): Promise<DocumentReference[]> {
return getDocuments(...params);
}
82 changes: 0 additions & 82 deletions packages/api/src/external/fhir/document/search-documents.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/api/src/routes/medical/document.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { UploadDocumentResult } from "@metriport/api-sdk";
import { createDocumentFilePath } from "@metriport/core/domain/document/filename";
import { S3Utils } from "@metriport/core/external/aws/s3";
import { searchDocuments } from "@metriport/core/external/opensearch/search-documents";
import { uuidv7 } from "@metriport/core/util/uuid-v7";
import { stringToBoolean } from "@metriport/shared";
import { Request, Response } from "express";
Expand All @@ -18,14 +19,13 @@ import {
docRefCheck,
} from "../../external/fhir/document/draft-update-document-reference";
import { upsertDocumentToFHIRServer } from "../../external/fhir/document/save-document-reference";
import { searchDocuments } from "../../external/fhir/document/search-documents";
import { Config } from "../../shared/config";
import { requestLogger } from "../helpers/request-logger";
import { sanitize } from "../helpers/string";
import { optionalDateSchema } from "../schemas/date";
import { asyncHandler, getCxIdOrFail, getFrom, getFromQueryOrFail } from "../util";
import { toDTO } from "./dtos/documentDTO";
import { docConversionTypeSchema, docFileNameSchema } from "./schemas/documents";
import { requestLogger } from "../helpers/request-logger";
import { cxRequestMetadataSchema } from "./schemas/request-metadata";

const router = Router();
Expand Down
23 changes: 12 additions & 11 deletions packages/core/src/external/aws/document-signing/bulk-sign.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { S3Utils } from "../s3";
import { DocumentReference } from "@medplum/fhirtypes";
import { searchDocuments } from "../../opensearch/search-documents";
import axios from "axios";
import { capture } from "../../../util/notifications";
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import { capture } from "../../../util/notifications";
import { searchDocuments } from "../../opensearch/search-documents";
import { S3Utils } from "../s3";
import { DocumentBulkSignerLambdaResponse } from "./document-bulk-signer-response";
const ossApi = axios.create();

dayjs.extend(duration);

const ossApi = axios.create();
const SIGNED_URL_DURATION_SECONDS = dayjs.duration({ minutes: 3 }).asSeconds();

export type DocumentBulkSignerLambdaRequest = {
Expand All @@ -17,7 +17,7 @@ export type DocumentBulkSignerLambdaRequest = {
requestId: string;
};

export async function getSignedUrls(
export async function searchDocumentsSignUrlsAndSendToApi(
cxId: string,
patientId: string,
requestId: string,
Expand All @@ -27,12 +27,12 @@ export async function getSignedUrls(
) {
const s3Utils = new S3Utils(region);

const documents: DocumentReference[] = await searchDocuments({ cxId, patientId });
const documents = await searchDocuments({ cxId, patientId });
const ossApiClient = apiClientBulkDownloadWebhook(apiURL);

try {
const urls = await Promise.all(
documents.map(async doc => {
documents.flatMap(async doc => {
const attachment = (doc?.content ?? [])
.map(content => content?.attachment)
.find(attachment => attachment?.title !== undefined);
Expand Down Expand Up @@ -62,7 +62,7 @@ export async function getSignedUrls(
})
);

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

await ossApiClient.callInternalEndpoint({
cxId: cxId,
Expand All @@ -72,8 +72,9 @@ export async function getSignedUrls(
status: "completed",
});
} catch (error) {
capture.error(error, {
extra: { patientId, context: `bulkUrlSigningLambda.getSignedUrls`, error },
const msg = "Error getting signed URLs";
capture.error(msg, {
extra: { patientId, context: `getSignedUrls`, error },
});
await ossApiClient.callInternalEndpoint({
cxId: cxId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getSignedUrls } from "./bulk-sign";
import { searchDocumentsSignUrlsAndSendToApi } from "./bulk-sign";
import { DocumentBulkSigner, DocumentBulkSignerRequest } from "./document-bulk-signer";

export class DocumentBulkSignerLocal extends DocumentBulkSigner {
Expand All @@ -7,6 +7,13 @@ export class DocumentBulkSignerLocal extends DocumentBulkSigner {
}

async sign({ patientId, cxId, requestId }: DocumentBulkSignerRequest): Promise<void> {
await getSignedUrls(cxId, patientId, requestId, this.bucketName, this.region, this.apiURL);
await searchDocumentsSignUrlsAndSendToApi(
cxId,
patientId,
requestId,
this.bucketName,
this.region,
this.apiURL
);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { faker } from "@faker-js/faker";
import { ISO_DATE } from "@metriport/shared/common/date";
import dayjs from "dayjs";
import { trim } from "lodash";
import { v4 as uuidv4 } from "uuid";
import { makePatient } from "../../../../domain/medical/__tests__/patient";
import { ISO_DATE } from "../../../../shared/date";
import { getFilters } from "../get-documents";

beforeEach(() => {
Expand All @@ -17,9 +17,23 @@ describe("getDocuments", () => {
});

it("returns patientId when only patientId is set", async () => {
const patient = makePatient();
const res = getFilters({ patientId: patient.id });
expect(res).toEqual("patient=" + patient.id);
const patientId = faker.string.uuid();
const res = getFilters({ patientId });
expect(res).toEqual("patient=" + patientId);
});

it("returns list of patientId when gets patientId as array", async () => {
const patientIds = [faker.string.uuid(), faker.string.uuid(), faker.string.uuid()];
const encodedPatientIds = encodeURIComponent(patientIds.join(","));
const res = getFilters({ patientId: patientIds });
expect(res).toEqual("patient=" + encodedPatientIds);
});

it("trims patientIds", async () => {
const patientIds = [faker.string.uuid() + " ", " " + faker.string.uuid()];
const encodedPatientIds = encodeURIComponent(patientIds.map(trim).join(","));
const res = getFilters({ patientId: patientIds });
expect(res).toEqual("patient=" + encodedPatientIds);
});

it("returns `from` when only `from` is set", async () => {
Expand All @@ -41,20 +55,20 @@ describe("getDocuments", () => {
});

it("groups patientId and documentIds when both are set", async () => {
const patient = makePatient();
const patientId = faker.string.uuid();
const documentIds = [uuidv4(), uuidv4()];
const res = getFilters({ patientId: patient.id, documentIds });
const res = getFilters({ patientId: patientId, documentIds });
expect(res).toEqual(
"patient=" + patient.id + "&_id=" + documentIds.join(encodeURIComponent(","))
"patient=" + patientId + "&_id=" + documentIds.join(encodeURIComponent(","))
);
});

it("groups patientId and dates when those are set", async () => {
const patient = makePatient();
const patientId = faker.string.uuid();
const from = dayjs(faker.date.past()).format(ISO_DATE);
const to = dayjs(faker.date.past()).format(ISO_DATE);
const res = getFilters({ patientId: patient.id, from, to });
expect(res).toEqual("patient=" + patient.id + "&date=ge" + from + "&date=le" + to);
const res = getFilters({ patientId, from, to });
expect(res).toEqual("patient=" + patientId + "&date=ge" + from + "&date=le" + to);
});

it("groups documentIds and dates when those are set", async () => {
Expand All @@ -68,14 +82,14 @@ describe("getDocuments", () => {
});

it("groups all when all are set", async () => {
const patient = makePatient();
const patientId = faker.string.uuid();
const documentIds = [uuidv4(), uuidv4()];
const from = dayjs(faker.date.past()).format(ISO_DATE);
const to = dayjs(faker.date.past()).format(ISO_DATE);
const res = getFilters({ patientId: patient.id, documentIds, from, to });
const res = getFilters({ patientId: patientId, documentIds, from, to });
expect(res).toEqual(
"patient=" +
patient.id +
patientId +
"&_id=" +
documentIds.join(encodeURIComponent(",")) +
"&date=ge" +
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { DocumentReference, Resource } from "@medplum/fhirtypes";

export type DocumentReferenceWithId = Omit<DocumentReference, "id"> &
Required<Pick<DocumentReference, "id">>;

export function hasId(docRef: DocumentReference): docRef is DocumentReferenceWithId {
return !!docRef.id;
}

export function isDocumentReference(
resource?: Resource | undefined
): resource is DocumentReference {
Expand Down
Loading
Loading