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

do some ts magic to get around the switch setup #46

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
91 changes: 28 additions & 63 deletions query-connector/src/app/query-service.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
"use server";
import fetch from "node-fetch";
import {
Patient,
Observation,
DiagnosticReport,
Condition,
Encounter,
Medication,
MedicationAdministration,
MedicationRequest,
Bundle,
} from "fhir/r4";
import { Bundle, DomainResource } from "fhir/r4";

import FHIRClient from "./fhir-servers";
import {
USE_CASES,
FHIR_SERVERS,
ValueSetItem,
isFhirResource,
FhirResource,
} from "./constants";
import { CustomQuery } from "./CustomQuery";
import { GetPhoneQueryFormats } from "./format-service";
Expand All @@ -27,14 +18,13 @@ import { formatValueSetItemsAsQuerySpec } from "./format-service";
* The query response when the request source is from the Viewer UI.
*/
export type QueryResponse = {
Patient?: Patient[];
Observation?: Observation[];
DiagnosticReport?: DiagnosticReport[];
Condition?: Condition[];
Encounter?: Encounter[];
Medication?: Medication[];
MedicationAdministration?: MedicationAdministration[];
MedicationRequest?: MedicationRequest[];
[R in FhirResource as R["resourceType"]]?: R[];
};

// workaround types to get around a typescript compilation issue
type SuperSetFhirResource = DomainResource | FhirResource;
type SuperSetQueryResponse = {
[R in SuperSetFhirResource as R["resourceType"]]?: R[];
};

export type APIQueryResponse = Bundle;
Expand Down Expand Up @@ -236,56 +226,26 @@ async function generalizedQuery(
*/
export async function parseFhirSearch(
response: fetch.Response | Array<fetch.Response>,
queryResponse: QueryResponse = {},
queryResponse: SuperSetQueryResponse = {},
): Promise<QueryResponse> {
let resourceArray: unknown[] = [];
let resourceArray: SuperSetFhirResource[] = [];

// Process the responses and flatten them
if (Array.isArray(response)) {
resourceArray = (await Promise.all(response.map(processResponse))).flat();
resourceArray = (
await Promise.all(response.map(processFhirResponse))
).flat();
} else {
resourceArray = await processResponse(response);
resourceArray = await processFhirResponse(response);
}

// Add resources to queryResponse
for (const resource of resourceArray) {
if (isFhirResource(resource)) {
switch (resource.resourceType) {
case "Patient":
queryResponse.Patient = queryResponse.Patient ?? [];
queryResponse.Patient.push(resource);
break;
case "Observation":
queryResponse.Observation = queryResponse.Observation ?? [];
queryResponse.Observation.push(resource);
break;
case "DiagnosticReport":
queryResponse.DiagnosticReport = queryResponse.DiagnosticReport ?? [];
queryResponse.DiagnosticReport.push(resource);
break;
case "Condition":
queryResponse.Condition = queryResponse.Condition ?? [];
queryResponse.Condition.push(resource);
break;
case "Encounter":
queryResponse.Encounter = queryResponse.Encounter ?? [];
queryResponse.Encounter.push(resource);
break;
case "Medication":
queryResponse.Medication = queryResponse.Medication ?? [];
queryResponse.Medication.push(resource);
break;
case "MedicationAdministration":
queryResponse.MedicationAdministration =
queryResponse.MedicationAdministration ?? [];
queryResponse.MedicationAdministration.push(resource);
break;
case "MedicationRequest":
queryResponse.MedicationRequest =
queryResponse.MedicationRequest ?? [];
queryResponse.MedicationRequest.push(resource);
break;
}
const resourceType = resource.resourceType;
if (!(resourceType in queryResponse)) {
queryResponse[resourceType] = [resource];
} else {
queryResponse[resourceType]!.push(resource);
}
}
return queryResponse;
Expand All @@ -297,14 +257,19 @@ export async function parseFhirSearch(
* @param response - The response from the FHIR server.
* @returns - The array of resources from the response.
*/
export async function processResponse(
export async function processFhirResponse(
response: fetch.Response,
): Promise<unknown[]> {
let resourceArray: unknown[] = [];
): Promise<FhirResource[]> {
let resourceArray: FhirResource[] = [];
Copy link
Collaborator Author

@fzhao99 fzhao99 Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robertandremitchell pushed the type checking from where you had it down a level to where we're making contact against the relevant FHIR response, which feels like the part of the stack where we should check for it. RN just logging out a mismatch in the event that we have an issue rather than "properly" handling the issue based on the scope of the ticket, but lmk if you have any issues here

Also renamed the function accordingly

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that makes sense. I had tinkered with moving it yesterday but it felt like it was getting more complex. this definitely simplifies it a lot, which is great!

if (response.status === 200) {
const body = await response.json();
if (body.entry) {
for (const entry of body.entry) {
if (!isFhirResource(entry.resource)) {
console.error(
"Entry in FHIR resource response parsing was of unexpected shape",
);
}
resourceArray.push(entry.resource);
}
}
Expand Down
4 changes: 2 additions & 2 deletions query-connector/src/app/tests/unit/query-service.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
QueryResponse,
processResponse,
processFhirResponse,
parseFhirSearch,
} from "@/app/query-service";
import { isFhirResource } from "@/app/constants";
Expand Down Expand Up @@ -34,7 +34,7 @@ describe("process response", () => {
status: 200,
json: async () => patientBundle,
} as unknown as fetch.Response;
const resourceArray = await processResponse(response);
const resourceArray = await processFhirResponse(response);

// Using isFhirResource
resourceArray.forEach((r) => {
Expand Down