Skip to content

Commit

Permalink
Merge pull request #45 from jembi/Fetch-patient-using-params
Browse files Browse the repository at this point in the history
Fetch patient using query params
  • Loading branch information
bradsawadye authored May 15, 2024
2 parents da5c402 + 8b178a0 commit f86c739
Show file tree
Hide file tree
Showing 3 changed files with 687 additions and 66 deletions.
202 changes: 202 additions & 0 deletions src/routes/handlers/fetchPatient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { Bundle } from 'fhir/r3';
import { getConfig } from '../../config/config';
import logger from '../../logger';
import { MpiMediatorResponseObject } from '../../types/response';
import { getMpiAuthToken } from '../../utils/mpi';
import {
getData,
isHttpStatusOk,
createNewPatientRef,
patientProjector,
createHandlerResponseObject,
} from '../../utils/utils';
import { Patient } from 'fhir/r3';

const {
fhirDatastoreProtocol: fhirProtocol,
fhirDatastoreHost: fhirHost,
fhirDatastorePort: fhirPort,
mpiProtocol: mpiProtocol,
mpiHost: mpiHost,
mpiPort: mpiPort,
mpiAuthEnabled,
} = getConfig();

export const fetchPatientByQuery = async (
query: object
): Promise<MpiMediatorResponseObject> => {
const combinedParams = new URLSearchParams(query as Record<string, string>).toString();

const headers: HeadersInit = {
'Content-Type': 'application/fhir+json',
};

if (mpiAuthEnabled) {
const token = await getMpiAuthToken();

headers['Authorization'] = `Bearer ${token.accessToken}`;
}

const mpiResponse = await getData(
mpiProtocol,
mpiHost,
mpiPort,
`/fhir/Patient?${combinedParams}`,
headers
);

const promises: any[] = [];

if (isHttpStatusOk(mpiResponse.status)) {
const bundle = mpiResponse.body as Bundle;

logger.debug(`Adding patient link FHIR store`);

addPatientLinks(promises, bundle);
} else {
return createHandlerResponseObject('Failed', mpiResponse);
}

try {
const entries = await Promise.all(promises);

return createHandlerResponseObject('Successful', {
status: 200,
body: {
resourceType: 'Bundle',
id: combinedParams,
type: 'searchset',
total: entries.length,
entries: entries,
},
});
} catch (err) {
const status = (err as any).status || 500;
const body = (err as any).body || {};

logger.error('Failed to retrieve patient ', body);

return createHandlerResponseObject('Failed', { status, body });
}
};

export const fetchPatientById = async (
requestedId: string,
projection: string
): Promise<MpiMediatorResponseObject> => {
const fhirResponse = await getData(
fhirProtocol,
fhirHost,
fhirPort,
`/fhir/Patient/${requestedId}`,
{}
);

let upstreamId = requestedId;

if (fhirResponse.status === 200) {
const patient = fhirResponse.body as Patient;
const interactionId =
patient.link && patient.link[0]?.other.reference?.match(/Patient\/([^/]+)/)?.[1];

if (interactionId) {
upstreamId = interactionId;
logger.debug(`Swapping source ID ${requestedId} for interaction ID ${upstreamId}`);
}
} else {
return createHandlerResponseObject('Failed', fhirResponse);
}

logger.debug(`Fetching patient ${upstreamId} from MPI`);

const headers: HeadersInit = {
'Content-Type': 'application/fhir+json',
};

if (mpiAuthEnabled) {
const token = await getMpiAuthToken();

headers['Authorization'] = `Bearer ${token.accessToken}`;
}

const mpiResponse = await getData(
mpiProtocol,
mpiHost,
mpiPort,
`/fhir/links/Patient/${upstreamId}`,
headers
);

let transactionStatus = 'Successful';

// Map the upstreamId to the requestedId
if (mpiResponse.status === 200) {
const patient = mpiResponse.body as Patient;

patient.id = requestedId;

if (projection === 'partial') mpiResponse.body = patientProjector(patient);

logger.debug(
`Mapped upstream ID ${upstreamId} to requested ID ${requestedId} in response body`
);
} else {
transactionStatus = 'Failed';
}

return createHandlerResponseObject(transactionStatus, mpiResponse);
};

const addPatientLinks = (promises: any[], bundle: Bundle): void => {
bundle.entry?.forEach((patient, index) => {
const promise = new Promise(async (resolve, reject) => {
const mpiLinksResponse = await getData(
mpiProtocol,
mpiHost,
mpiPort,
`/fhir/Patient/${encodeURIComponent(patient.resource?.id || '')}`,
{}
);

if (isHttpStatusOk(mpiLinksResponse.status)) {
const patient = mpiLinksResponse.body as Patient;
const links =
patient.link?.map((link) =>
createNewPatientRef(link.other.reference?.split('/').pop() || '')
) || [];

const fhirResponse = await getData(
fhirProtocol,
fhirHost,
fhirPort,
`/fhir/Patient?link=${encodeURIComponent(links.join(','))}`,
{}
);

if (!isHttpStatusOk(fhirResponse.status)) {
reject(fhirResponse);
}

const fhirBundle = fhirResponse.body as Bundle;

if (bundle.entry && bundle.entry[index] && fhirBundle.entry) {
const links = fhirBundle.entry.map((entry) => {
return {
type: 'refer',
other: { reference: `Patient/${entry?.resource?.id || ''}` },
};
});
resolve({
fullUrl: fhirBundle.entry[index].fullUrl,
resource: { ...bundle.entry[index].resource, link: links },
request: fhirBundle.entry[index].request,
});
}
} else {
reject(mpiLinksResponse);
}
});

promises.push(promise);
});
};
83 changes: 17 additions & 66 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import { matchAsyncHandler } from './handlers/matchPatientAsync';
import { matchSyncHandler } from './handlers/matchPatientSync';
import { mpiMdmQueryLinksMiddleware } from '../middlewares/mpi-mdm-query-links';
import { validationMiddleware } from '../middlewares/validation';
import { buildOpenhimResponseObject, getData, patientProjector } from '../utils/utils';
import { buildOpenhimResponseObject } from '../utils/utils';
import { fetchEverythingByRef } from './handlers/fetchPatientResources';
import { mpiMdmSummaryMiddleware } from '../middlewares/mpi-mdm-summary';
import { fetchPatientSummaryByRef } from './handlers/fetchPatientSummaries';
import { getConfig } from '../config/config';
import { Patient } from 'fhir/r3';
import { getMpiAuthToken } from '../utils/mpi';
import logger from '../logger';
import {
fetchPatientById,
fetchPatientByQuery,
} from './handlers/fetchPatient';

const routes = express.Router();

Expand Down Expand Up @@ -70,70 +72,19 @@ routes.get('/fhir/Patient/:patientId', async (req, res) => {

logger.debug(`Fetching patient ${requestedId} from FHIR store`);

const {
fhirDatastoreProtocol: fhirProtocol,
fhirDatastoreHost: fhirHost,
fhirDatastorePort: fhirPort,
mpiProtocol: mpiProtocol,
mpiHost: mpiHost,
mpiPort: mpiPort,
mpiAuthEnabled,
} = getConfig();
const fhirResponse = await getData(
fhirProtocol,
fhirHost,
fhirPort,
`/fhir/Patient/${requestedId}`,
{}
);

let upstreamId = requestedId;

if (fhirResponse.status === 200) {
const patient = fhirResponse.body as Patient;
const interactionId =
patient.link && patient.link[0]?.other.reference?.match(/Patient\/([^/]+)/)?.[1];

if (interactionId) {
upstreamId = interactionId;
logger.debug(`Swapping source ID ${requestedId} for interaction ID ${upstreamId}`);
}
}

logger.debug(`Fetching patient ${upstreamId} from MPI`);

const headers: HeadersInit = {
'Content-Type': 'application/fhir+json',
};

if (mpiAuthEnabled) {
const token = await getMpiAuthToken();

headers['Authorization'] = `Bearer ${token.accessToken}`;
}

const mpiResponse = await getData(
mpiProtocol,
mpiHost,
mpiPort,
`/fhir/links/Patient/${upstreamId}`,
{}
);

// Map the upstreamId to the requestedId
if (mpiResponse.status === 200) {
const patient = mpiResponse.body as Patient;

patient.id = requestedId;

if (req.query.projection === 'partial') mpiResponse.body = patientProjector(patient);

logger.debug(
`Mapped upstream ID ${upstreamId} to requested ID ${requestedId} in response body`
);
}
const { status, body } = await fetchPatientById(requestedId, String(req.query?.projection));

res.set('Content-Type', 'application/json+openhim');
res.status(status).send(body);
});

routes.get('/fhir/Patient', async (req, res) => {
logger.debug(`Fetching patient from Client registry using query params`);

const { status, body } = await fetchPatientByQuery(req.query);

res.status(mpiResponse.status).send(mpiResponse.body);
res.set('Content-Type', 'application/json+openhim');
res.status(status).send(body);
});

routes.get(
Expand Down
Loading

0 comments on commit f86c739

Please sign in to comment.