From c7b6de308785df5d7a5a672dcf5ece76cf0617bb Mon Sep 17 00:00:00 2001 From: Ryan Crichton Date: Mon, 4 Mar 2024 15:19:16 +0200 Subject: [PATCH 1/2] Fix content type and body handling for OpenHIM responses --- package-lock.json | 4 ++-- package.json | 2 +- src/middlewares/mpi-mdm-everything.ts | 2 +- src/middlewares/mpi-mdm-summary.ts | 2 +- src/middlewares/openhim-proxy-interceptor.ts | 2 ++ src/middlewares/validation.ts | 13 +++++++++---- src/routes/index.ts | 10 +++++----- src/types/response.ts | 2 +- src/utils/utils.ts | 4 ++-- 9 files changed, 24 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2c4d545..cea1724 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mpi-mediator", - "version": "v2.0.0", + "version": "v2.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mpi-mediator", - "version": "v2.0.0", + "version": "v2.0.2", "license": "ISC", "dependencies": { "@types/sinon": "^10.0.13", diff --git a/package.json b/package.json index 021e3d2..e83c68b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mpi-mediator", - "version": "v2.0.1", + "version": "v2.0.2", "description": "An OpenHIM mediator to handle all interactions with an MPI component", "main": "index.ts", "scripts": { diff --git a/src/middlewares/mpi-mdm-everything.ts b/src/middlewares/mpi-mdm-everything.ts index 883bd47..42bb519 100644 --- a/src/middlewares/mpi-mdm-everything.ts +++ b/src/middlewares/mpi-mdm-everything.ts @@ -69,6 +69,6 @@ export const mpiMdmEverythingMiddleware: RequestHandler = async (req, res, next) const { status, body } = await fetchAllLinkedPatientResources(req.params.patientId); - res.set('Content-Type', 'application/openhim+json'); + res.set('Content-Type', 'application/json+openhim'); res.status(status).send(body); }; diff --git a/src/middlewares/mpi-mdm-summary.ts b/src/middlewares/mpi-mdm-summary.ts index bb51d91..859c530 100644 --- a/src/middlewares/mpi-mdm-summary.ts +++ b/src/middlewares/mpi-mdm-summary.ts @@ -42,6 +42,6 @@ export const mpiMdmSummaryMiddleware: RequestHandler = async (req, res, next) => const { status, body } = await fetchAllLinkedPatientSummary(req.params.patientId); - res.set('Content-Type', 'application/openhim+json'); + res.set('Content-Type', 'application/json+openhim'); res.status(status).send(body); }; diff --git a/src/middlewares/openhim-proxy-interceptor.ts b/src/middlewares/openhim-proxy-interceptor.ts index f5da736..9e21f50 100644 --- a/src/middlewares/openhim-proxy-interceptor.ts +++ b/src/middlewares/openhim-proxy-interceptor.ts @@ -24,6 +24,8 @@ export const openhimProxyResponseInterceptor = responseInterceptor( const responseBody = buildOpenhimResponseObject(transactionStatus, statusCode, body); + _res.setHeader('Content-Type', 'application/json+openhim'); + return JSON.stringify(responseBody); } ); diff --git a/src/middlewares/validation.ts b/src/middlewares/validation.ts index 7b7e73d..e748877 100644 --- a/src/middlewares/validation.ts +++ b/src/middlewares/validation.ts @@ -4,8 +4,13 @@ import logger from '../logger'; import { ResponseObject } from '../types/response'; import { buildOpenhimResponseObject, isHttpStatusOk, postData } from '../utils/utils'; -const { fhirDatastoreProtocol, fhirDatastoreHost, fhirDatastorePort, contentType, disableValidation } = - getConfig(); +const { + fhirDatastoreProtocol, + fhirDatastoreHost, + fhirDatastorePort, + contentType, + disableValidation, +} = getConfig(); export const validationMiddleware: RequestHandler = async (req, res, next) => { logger.info('Validating Fhir Resources'); @@ -21,7 +26,7 @@ export const validationMiddleware: RequestHandler = async (req, res, next) => { status: 400, }; } else if (disableValidation) { - return next(); + return next(); } else { response = await postData( fhirDatastoreProtocol, @@ -56,6 +61,6 @@ export const validationMiddleware: RequestHandler = async (req, res, next) => { response.body ); - res.set('Content-Type', 'application/openhim+json'); + res.set('Content-Type', 'application/json+openhim'); res.status(response.status).send(responseBody); }; diff --git a/src/routes/index.ts b/src/routes/index.ts index da35f7c..91bc49b 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -26,7 +26,7 @@ routes.post( asyncHandler(async (req, res) => { const result = await matchSyncHandler(req.body); - res.set('Content-Type', 'application/openhim+json'); + res.set('Content-Type', 'application/json+openhim'); res.status(result.status).send(result.body); }) ); @@ -40,7 +40,7 @@ routes.post( const responseBody = buildOpenhimResponseObject(transactionStatus, status, body); - res.set('Content-Type', 'application/openhim+json'); + res.set('Content-Type', 'application/json+openhim'); res.status(status).send(responseBody); }) ); @@ -67,7 +67,7 @@ routes.get( asyncHandler(async (req, res) => { const { status, body } = await fetchEverythingByRef(`Patient/${req.params.patientId}`); - res.set('Content-Type', 'application/openhim+json'); + res.set('Content-Type', 'application/json+openhim'); res.status(status).send(body); }) ); @@ -78,7 +78,7 @@ routes.get( asyncHandler(async (req, res) => { const { status, body } = await fetchPatientSummaryByRef(`Patient/${req.params.patientId}`); - res.set('Content-Type', 'application/openhim+json'); + res.set('Content-Type', 'application/json+openhim'); res.status(status).send(body); }) ); @@ -90,7 +90,7 @@ routes.post( asyncHandler(async (req, res) => { const result = await matchAsyncHandler(req.body); - res.set('Content-Type', 'application/openhim+json'); + res.set('Content-Type', 'application/json+openhim'); res.status(result.status).send(result.body); }) ); diff --git a/src/types/response.ts b/src/types/response.ts index 88fac92..0fdb56c 100644 --- a/src/types/response.ts +++ b/src/types/response.ts @@ -1,6 +1,6 @@ export interface Response { status: number; - body: object; + body: string; timestamp: string; headers: HeadersInit; } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index a1b782b..cc60f14 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -95,12 +95,12 @@ export const buildOpenhimResponseObject = ( openhimTransactionStatus: string, httpResponseStatusCode: number, responseBody: object, - contentType = 'application/json' + contentType = 'application/fhir+json' ): OpenHimResponseObject => { const response: Response = { status: httpResponseStatusCode, headers: { 'Content-Type': contentType }, - body: responseBody, + body: JSON.stringify(responseBody), timestamp: format(new Date(), "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"), }; From 40b39b23fbe0cf564d954bf4c3558cf0c6cc8786 Mon Sep 17 00:00:00 2001 From: Ryan Crichton Date: Mon, 4 Mar 2024 15:48:47 +0200 Subject: [PATCH 2/2] Fix tests --- .../step-definitions/fhirAccessProxy.ts | 6 +++--- .../step-definitions/mpiAuthentication.ts | 9 +++++---- .../step-definitions/patientSyncMatching.ts | 2 +- tests/unit/fetchPatientResources.ts | 4 ++-- tests/unit/fetchPatientSummaries.ts | 4 ++-- tests/unit/kafkaAsyncPatientHandler.ts | 6 +++--- tests/unit/matchPatientSync.ts | 10 ++++++---- tests/unit/middlewares.ts | 20 ++++++------------- tests/unit/utils.ts | 4 ++-- 9 files changed, 30 insertions(+), 35 deletions(-) diff --git a/tests/cucumber/step-definitions/fhirAccessProxy.ts b/tests/cucumber/step-definitions/fhirAccessProxy.ts index 30bc251..171cc55 100644 --- a/tests/cucumber/step-definitions/fhirAccessProxy.ts +++ b/tests/cucumber/step-definitions/fhirAccessProxy.ts @@ -41,7 +41,7 @@ Then( 'a successful response containing a bundle of related patient resources is sent back', (): void => { expect(responseBody.status).to.equal('Success'); - expect(responseBody.response.body.resourceType).to.equal('Bundle'); + expect(JSON.parse(responseBody.response.body).resourceType).to.equal('Bundle'); server.close(); } ); @@ -55,7 +55,7 @@ When( .set('content-type', 'application/fhir+json') .expect(200); - const { response } = bundleSubmission.body.response.body.entry.find((entry) => + const { response } = JSON.parse(bundleSubmission.body.response.body).entry.find((entry) => entry.response.location.startsWith('Patient') ); @@ -71,7 +71,7 @@ When( Then('a successful response containing a bundle is sent back', (): void => { expect(responseBody.status).to.equal('Success'); - expect(responseBody.response.body.resourceType).to.equal('Bundle'); + expect(JSON.parse(responseBody.response.body).resourceType).to.equal('Bundle'); server.close(); }); diff --git a/tests/cucumber/step-definitions/mpiAuthentication.ts b/tests/cucumber/step-definitions/mpiAuthentication.ts index 969bccb..8d034d8 100644 --- a/tests/cucumber/step-definitions/mpiAuthentication.ts +++ b/tests/cucumber/step-definitions/mpiAuthentication.ts @@ -33,7 +33,8 @@ When('a post request without body was sent to get patients', async (): Promise { - expect(responseBody.response.body.error, + expect( + responseBody.response.body.error, `Invalid Content! Type should be "${config.contentType}" and Length should be greater than 0"` ); server.close(); @@ -51,8 +52,8 @@ When('a post request with body was sent to get patients', async (): PromisePatient', - status: 'generated' + div: '
Patient
', + status: 'generated', }, name: [ { @@ -79,6 +80,6 @@ When('a post request with body was sent to get patients', async (): Promise { - expect(responseBody.response.body.id).not.empty; + expect(JSON.parse(responseBody.response.body).id).not.empty; server.close(); }); diff --git a/tests/cucumber/step-definitions/patientSyncMatching.ts b/tests/cucumber/step-definitions/patientSyncMatching.ts index 35e8cbf..b9a23ef 100644 --- a/tests/cucumber/step-definitions/patientSyncMatching.ts +++ b/tests/cucumber/step-definitions/patientSyncMatching.ts @@ -97,7 +97,7 @@ When( Then('a patient should be created on the client registry', async (): Promise => { const auth = await getMpiAuthToken(); - const { response } = responseBody.response.body.entry.find((entry) => + const { response } = JSON.parse(responseBody.response.body).entry.find((entry) => entry.response.location.startsWith('Patient') ); diff --git a/tests/unit/fetchPatientResources.ts b/tests/unit/fetchPatientResources.ts index 40d90b4..f453376 100644 --- a/tests/unit/fetchPatientResources.ts +++ b/tests/unit/fetchPatientResources.ts @@ -136,7 +136,7 @@ describe('FetchPatientResources handler', (): void => { .reply(200, {}); const result: MpiMediatorResponseObject = await fetchEverythingByRef(patientRef); - expect(result.body.response.body).to.deep.equal(emptyBundle); + expect(JSON.parse(result.body.response.body)).to.deep.equal(emptyBundle); }); it('should succesfully return bundle of various resources', async (): Promise => { @@ -149,7 +149,7 @@ describe('FetchPatientResources handler', (): void => { const result: MpiMediatorResponseObject = await fetchEverythingByRef(patientRef); - expect(result.body.response.body).to.deep.equal(bundle); + expect(JSON.parse(result.body.response.body)).to.deep.equal(bundle); }); }); }); diff --git a/tests/unit/fetchPatientSummaries.ts b/tests/unit/fetchPatientSummaries.ts index 4aa30aa..79fa320 100644 --- a/tests/unit/fetchPatientSummaries.ts +++ b/tests/unit/fetchPatientSummaries.ts @@ -213,13 +213,13 @@ describe('FetchPatientSummaries handler', (): void => { nock(fhirDatastoreUrl).get(`/fhir/${emptyPatientRef1}/$summary`).reply(200, {}); const result = await fetchPatientSummaryByRef(emptyPatientRef1); - expect(result.body.response.body).to.deep.equal(emptyBundle); + expect(JSON.parse(result.body.response.body)).to.deep.equal(emptyBundle); }); it('should return a bundle with 2 entries for 1 given patient', async () => { nock(fhirDatastoreUrl).get(`/fhir/${patientRef3}/$summary`).reply(200, patientSummary3); const result = await fetchPatientSummaryByRef(patientRef3); - expect(result.body.response.body).to.deep.equal(combinedBundle2); + expect(JSON.parse(result.body.response.body)).to.deep.equal(combinedBundle2); }); }); }); diff --git a/tests/unit/kafkaAsyncPatientHandler.ts b/tests/unit/kafkaAsyncPatientHandler.ts index 108a1da..3ce5db4 100644 --- a/tests/unit/kafkaAsyncPatientHandler.ts +++ b/tests/unit/kafkaAsyncPatientHandler.ts @@ -46,7 +46,7 @@ describe('Kafka Async Patient Handler', (): void => { 'x-mediator-urn': '123', response: { status: 200, - body: {}, + body: '', timestamp: '12-12-2012', headers: { 'content-type': 'application/json', @@ -111,7 +111,7 @@ describe('Kafka Async Patient Handler', (): void => { 'x-mediator-urn': '123', response: { status: 200, - body: {}, + body: '', timestamp: '12-12-2012', headers: { 'content-type': 'application/json', @@ -201,7 +201,7 @@ describe('Kafka Async Patient Handler', (): void => { 'x-mediator-urn': '123', response: { status: 200, - body: {}, + body: '', timestamp: '12-12-2012', headers: { 'content-type': 'application/json', diff --git a/tests/unit/matchPatientSync.ts b/tests/unit/matchPatientSync.ts index 52a640a..8a653fb 100644 --- a/tests/unit/matchPatientSync.ts +++ b/tests/unit/matchPatientSync.ts @@ -109,7 +109,7 @@ describe('Match Patient Synchronously', (): void => { response: { status: 200, headers: { 'content-type': 'application/json' }, - body: { mesage: 'Success' }, + body: '', timestamp: '12/02/1991', }, }, @@ -179,7 +179,9 @@ describe('Match Patient Synchronously', (): void => { expect(handlerResponse.status).to.be.equal(500); expect(handlerResponse.body.status).to.be.equal('Failed'); - expect(handlerResponse.body.response.body).to.deep.equal({ errors: [error] }); + expect(JSON.parse(handlerResponse.body.response.body)).to.deep.equal({ + errors: [error], + }); stub.restore(); }); @@ -254,7 +256,7 @@ describe('Match Patient Synchronously', (): void => { response: { status: 200, headers: { 'content-type': 'application/json' }, - body: { mesage: 'Success' }, + body: '', timestamp: '12/02/1991', }, }, @@ -420,7 +422,7 @@ describe('Match Patient Synchronously', (): void => { response: { status: 200, headers: { 'Content-Type': 'application/json' }, - body: { mesage: 'Success' }, + body: '', timestamp: '12/02/1991', }, }, diff --git a/tests/unit/middlewares.ts b/tests/unit/middlewares.ts index 2263c3c..c2fabc3 100644 --- a/tests/unit/middlewares.ts +++ b/tests/unit/middlewares.ts @@ -327,18 +327,10 @@ describe('Middlewares', (): void => { nock(mpiUrl).get('/fhir/Patient/1').reply(200, patientFhirResource1); nock(mpiUrl).get('/fhir/Patient/2').reply(200, patientFhirResource2); nock(fhirDatastoreUrl) - .get( - `/fhir/Encounter?subject=${encodeURIComponent( - 'Patient/1,Patient/2' - )}` - ) + .get(`/fhir/Encounter?subject=${encodeURIComponent('Patient/1,Patient/2')}`) .reply(200, Encounters); nock(fhirDatastoreUrl) - .get( - `/fhir/Observation?subject=${encodeURIComponent( - 'Patient/1,Patient/2' - )}` - ) + .get(`/fhir/Observation?subject=${encodeURIComponent('Patient/1,Patient/2')}`) .reply(200, Observations); const request = { @@ -362,8 +354,8 @@ describe('Middlewares', (): void => { await mpiMdmEverythingMiddleware(request, response, () => {}); expect(statusCode).to.equal(200); expect(result.status).to.equal('Success'); - expect(result.response.body.total).to.equal(4); - expect(result.response.body.entry.length).to.equal(4); + expect(JSON.parse(result.response.body).total).to.equal(4); + expect(JSON.parse(result.response.body).entry.length).to.equal(4); nock.cleanAll(); }); }); @@ -424,8 +416,8 @@ describe('Middlewares', (): void => { await mpiMdmSummaryMiddleware(request, response, () => {}); expect(statusCode).to.equal(200); expect(result.status).to.equal('Success'); - expect(result.response.body.total).to.equal(2); - expect(result.response.body.entry.length).to.equal(2); + expect(JSON.parse(result.response.body).total).to.equal(2); + expect(JSON.parse(result.response.body).entry.length).to.equal(2); nock.cleanAll(); }); }); diff --git a/tests/unit/utils.ts b/tests/unit/utils.ts index beb3010..1c582fb 100644 --- a/tests/unit/utils.ts +++ b/tests/unit/utils.ts @@ -47,7 +47,7 @@ describe('Utils', (): void => { expect(returnedObject.response.headers).to.deep.equal({ 'Content-Type': contentType, }); - expect(returnedObject.response.body).to.deep.equal(body); + expect(JSON.parse(returnedObject.response.body)).to.deep.equal(body); }); }); @@ -468,7 +468,7 @@ describe('Utils', (): void => { ); expect(handlerResponse.status).to.equal(200); - expect(handlerResponse.body.response.body).to.deep.equal({ + expect(JSON.parse(handlerResponse.body.response.body)).to.deep.equal({ message: 'Success', }); });