From d31b03829952de6aeb280cff0af7fa23d240eb21 Mon Sep 17 00:00:00 2001 From: Maria Lorena Rodriguez Viruel Date: Fri, 8 Nov 2024 20:48:50 -0300 Subject: [PATCH 1/4] fix(#123): add validation for openmrs id and search patient in openmrs --- mediator/test/workflows.spec.ts | 41 +++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/mediator/test/workflows.spec.ts b/mediator/test/workflows.spec.ts index baba0aa7..7612a600 100644 --- a/mediator/test/workflows.spec.ts +++ b/mediator/test/workflows.spec.ts @@ -117,27 +117,24 @@ describe('Workflows', () => { const checkMediatorResponse = await request(FHIR.url) .get('/mediator/') .auth(FHIR.username, FHIR.password); - expect(checkMediatorResponse.status).toBe(200); expect(checkMediatorResponse.body.status).toBe('success'); - const patient = PatientFactory.build({}, { name: 'OpenMRS patient', placeId: placeId }); + const patient = PatientFactory.build({name: 'OpenMRS Patient', phone: '+2548277217095'}, { placeId: placeId }); const createPatientResponse = await request(CHT.url) .post('/api/v1/people') .auth(chwUserName, chwPassword) .send(patient); - expect(createPatientResponse.status).toBe(200); expect(createPatientResponse.body.ok).toEqual(true); patientId = createPatientResponse.body.id; - await new Promise((r) => setTimeout(r, 5000)); + await new Promise((r) => setTimeout(r, 10000)); const retrieveFhirPatientIdResponse = await request(FHIR.url) .get('/fhir/Patient/?identifier=' + patientId) .auth(FHIR.username, FHIR.password); - expect(retrieveFhirPatientIdResponse.status).toBe(200); expect(retrieveFhirPatientIdResponse.body.total).toBe(1); @@ -145,21 +142,45 @@ describe('Workflows', () => { .get('/mediator/openmrs/sync') .auth(FHIR.username, FHIR.password) .send(); - expect(triggerOpenMrsSyncPatientResponse.status).toBe(200); - await new Promise((r) => setTimeout(r, 5000)); + await new Promise((r) => setTimeout(r, 10000)); const retrieveOpenMrsPatientIdResponse = await request(OPENMRS.url) .get('/Patient/?identifier=' + patientId) .auth(OPENMRS.username, OPENMRS.password); - expect(retrieveOpenMrsPatientIdResponse.status).toBe(200); - //this should work after fixing openmrs to have latest fhir omod and cht identifier defined. expect(retrieveOpenMrsPatientIdResponse.body.total).toBe(1); - //Validate HAPI updated ids + const openMrsPatientId = retrieveOpenMrsPatientIdResponse.body.entry[0].resource.id; + console.log('openMrsPatientId: ' + openMrsPatientId); + const retrieveUpdatedFhirPatientResponse = await request(FHIR.url) + .get(`/fhir/Patient/${patientId}`) + .auth(FHIR.username, FHIR.password); + console.log('retrieveUpdatedFhirPatientResponse.body:'); + console.log(JSON.stringify(retrieveUpdatedFhirPatientResponse.body)); + expect(retrieveUpdatedFhirPatientResponse.status).toBe(200); + expect(retrieveUpdatedFhirPatientResponse.body.identifier).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + value: openMrsPatientId, + }) + ]) + ); + + const searchOpenMrsPatientResponse = await request(OPENMRS.url) + .get(`/Patient/?given=OpenMRS&family=Patient`) + .auth(OPENMRS.username, OPENMRS.password); + expect(searchOpenMrsPatientResponse.status).toBe(200); + expect(searchOpenMrsPatientResponse.body.total).toBe(1); + expect(searchOpenMrsPatientResponse.body.entry[0].resource.id).toBe(openMrsPatientId); + const searchOpenMrsPatientbyPhoneResponse = await request(OPENMRS.url) + .get(`/Patient/?telecom.value=+2548277217095`) + .auth(OPENMRS.username, OPENMRS.password); + expect(searchOpenMrsPatientbyPhoneResponse.status).toBe(200); + expect(searchOpenMrsPatientbyPhoneResponse.body.total).toBe(1); + expect(searchOpenMrsPatientbyPhoneResponse.body.entry[0].resource.id).toBe(openMrsPatientId); }); //skipping this test because is incomplete. From f94d70998eb6577884eef6303e01453c47e92bde Mon Sep 17 00:00:00 2001 From: Maria Lorena Rodriguez Viruel Date: Fri, 22 Nov 2024 15:18:31 -0300 Subject: [PATCH 2/4] fix(#123): fixing Openmrs patient to cht --- mediator/test/openmrs-resource-factories.ts | 67 +++++++++++++++++++++ mediator/test/workflows.spec.ts | 52 +++++++++------- 2 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 mediator/test/openmrs-resource-factories.ts diff --git a/mediator/test/openmrs-resource-factories.ts b/mediator/test/openmrs-resource-factories.ts new file mode 100644 index 00000000..39e164dc --- /dev/null +++ b/mediator/test/openmrs-resource-factories.ts @@ -0,0 +1,67 @@ +import { randomUUID } from 'crypto'; +import { Factory } from 'rosie'; + +export const OpenMRSPatientFactory = new Factory() + .option('placeId') + .attr('resourceType', 'Patient') + .attr('id', () => randomUUID()) + .attr('meta', () => ({ + versionId: '2', + lastUpdated: new Date().toISOString(), + source: 'cht#rjEgeBRWROBrChB7' + })) + .attr('text', () => ({ + status: 'generated', + div: '
OpenMRS PATIENT
Identifier52802
Date of birth06 June 1980
' + })) + .attr('identifier', () => [ + { + id: randomUUID(), + use: 'official', + type: { text: 'CHT Patient ID' }, + value: Math.floor(Math.random() * 100000).toString() + }, + { + id: randomUUID(), + use: 'secondary', + type: { text: 'CHT Document ID' }, + value: randomUUID() + }, + { + id: randomUUID(), + use: 'secondary', + type: { text: 'OpenMRS Patient UUID' }, + value: randomUUID() + } + ]) + .attr('name', () => [ + { + id: randomUUID(), + family: 'Patient', + given: ['OpenMRS'] + } + ]) + .attr('telecom', () => [ + { + id: randomUUID(), + value: '+2548277217095' + } + ]) + .attr('gender', 'male') + .attr('birthDate', '1980-06-06') + .attr('address', ['placeId'], (placeId) => [ + { + id: randomUUID(), + line: ['123 Main St'], + city: 'Nairobi', + country: 'Kenya', + extension: [{ + extension: [ + { + url: 'http://fhir.openmrs.org/ext/address#address4', + valueString: `FCHV Area [${placeId}]` + } + ] + }] + } + ]); diff --git a/mediator/test/workflows.spec.ts b/mediator/test/workflows.spec.ts index 7612a600..74f4a55b 100644 --- a/mediator/test/workflows.spec.ts +++ b/mediator/test/workflows.spec.ts @@ -8,6 +8,8 @@ import { OrganizationFactory as OrganizationFactoryBase, ServiceRequestFactory as ServiceRequestFactoryBase } from '../src/middlewares/schemas/tests/fhir-resource-factories'; +import { OpenMRSPatientFactory } from './openmrs-resource-factories'; + const { generateAuthHeaders } = require('../../configurator/libs/authentication'); jest.setTimeout(50000); @@ -112,8 +114,8 @@ describe('Workflows', () => { await createOpenMRSIdType('CHT Document ID'); }); - describe('OpenMRS workflow', () => { - it('Should follow the CHT Patient to OpenMRS workflow', async () => { + describe.only('OpenMRS workflow', () => { + it('should follow the CHT Patient to OpenMRS workflow', async () => { const checkMediatorResponse = await request(FHIR.url) .get('/mediator/') .auth(FHIR.username, FHIR.password); @@ -153,7 +155,6 @@ describe('Workflows', () => { expect(retrieveOpenMrsPatientIdResponse.body.total).toBe(1); const openMrsPatientId = retrieveOpenMrsPatientIdResponse.body.entry[0].resource.id; - console.log('openMrsPatientId: ' + openMrsPatientId); const retrieveUpdatedFhirPatientResponse = await request(FHIR.url) .get(`/fhir/Patient/${patientId}`) .auth(FHIR.username, FHIR.password); @@ -174,35 +175,44 @@ describe('Workflows', () => { expect(searchOpenMrsPatientResponse.status).toBe(200); expect(searchOpenMrsPatientResponse.body.total).toBe(1); expect(searchOpenMrsPatientResponse.body.entry[0].resource.id).toBe(openMrsPatientId); - - const searchOpenMrsPatientbyPhoneResponse = await request(OPENMRS.url) - .get(`/Patient/?telecom.value=+2548277217095`) - .auth(OPENMRS.username, OPENMRS.password); - expect(searchOpenMrsPatientbyPhoneResponse.status).toBe(200); - expect(searchOpenMrsPatientbyPhoneResponse.body.total).toBe(1); - expect(searchOpenMrsPatientbyPhoneResponse.body.entry[0].resource.id).toBe(openMrsPatientId); }); - //skipping this test because is incomplete. - it.skip('Should follow the OpenMRS Patient to CHT workflow', async () => { + it('should follow the OpenMRS Patient to CHT workflow', async () => { const checkMediatorResponse = await request(FHIR.url) .get('/mediator/') .auth(FHIR.username, FHIR.password); - expect(checkMediatorResponse.status).toBe(200); - //TODO: Create a patient using openMRS api + console.log('placeId:' + placeId); + const openMrsPatient = OpenMRSPatientFactory.build({}, {placeId}); + console.log('openMrsPatient: ' + JSON.stringify(openMrsPatient)); + + const createOpenMrsPatientResponse = await request(OPENMRS.url) + .post('/Patient') + .auth(OPENMRS.username, OPENMRS.password) + .send(openMrsPatient); + + console.log('createOpenMrsPatientResponse: ' + JSON.stringify(createOpenMrsPatientResponse.body)); + expect(createOpenMrsPatientResponse.status).toBe(201); + expect(createOpenMrsPatientResponse.body).toHaveProperty('id'); + + const openMrsPatientId = createOpenMrsPatientResponse.body.id; + + await new Promise((r) => setTimeout(r, 10000)); + + const triggerOpenMrsSyncPatientResponse = await request('https://localhost:5002') + .get('/mediator/openmrs/sync') + .auth(OPENMRS.username, OPENMRS.password) + .send(); + expect(triggerOpenMrsSyncPatientResponse.status).toBe(200); + + await new Promise((r) => setTimeout(r, 10000)); const retrieveFhirPatientIdResponse = await request(FHIR.url) - .get('/fhir/Patient/?identifier=' + patientId) + .get('/fhir/Patient/?identifier=' + openMrsPatientId) .auth(FHIR.username, FHIR.password); - expect(retrieveFhirPatientIdResponse.status).toBe(200); - //expect(retrieveFhirPatientIdResponse.body.total).toBe(1); - - //TODO: retrieve and validate patient from CHT api - //trigger openmrs sync - //validate id + expect(retrieveFhirPatientIdResponse.body.total).toBe(1); }); }); From 68dd2f90727eb0e5a9a13b9a507640e83bbdcd4d Mon Sep 17 00:00:00 2001 From: Maria Lorena Rodriguez Viruel Date: Wed, 27 Nov 2024 08:52:40 -0300 Subject: [PATCH 3/4] fix(#123): complete openmrs patient to cht workflow --- mediator/test/workflows.spec.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mediator/test/workflows.spec.ts b/mediator/test/workflows.spec.ts index 74f4a55b..e2b9ed78 100644 --- a/mediator/test/workflows.spec.ts +++ b/mediator/test/workflows.spec.ts @@ -114,7 +114,7 @@ describe('Workflows', () => { await createOpenMRSIdType('CHT Document ID'); }); - describe.only('OpenMRS workflow', () => { + describe('OpenMRS workflow', () => { it('should follow the CHT Patient to OpenMRS workflow', async () => { const checkMediatorResponse = await request(FHIR.url) .get('/mediator/') @@ -158,8 +158,6 @@ describe('Workflows', () => { const retrieveUpdatedFhirPatientResponse = await request(FHIR.url) .get(`/fhir/Patient/${patientId}`) .auth(FHIR.username, FHIR.password); - console.log('retrieveUpdatedFhirPatientResponse.body:'); - console.log(JSON.stringify(retrieveUpdatedFhirPatientResponse.body)); expect(retrieveUpdatedFhirPatientResponse.status).toBe(200); expect(retrieveUpdatedFhirPatientResponse.body.identifier).toEqual( expect.arrayContaining([ @@ -183,16 +181,13 @@ describe('Workflows', () => { .auth(FHIR.username, FHIR.password); expect(checkMediatorResponse.status).toBe(200); - console.log('placeId:' + placeId); const openMrsPatient = OpenMRSPatientFactory.build({}, {placeId}); - console.log('openMrsPatient: ' + JSON.stringify(openMrsPatient)); const createOpenMrsPatientResponse = await request(OPENMRS.url) .post('/Patient') .auth(OPENMRS.username, OPENMRS.password) .send(openMrsPatient); - console.log('createOpenMrsPatientResponse: ' + JSON.stringify(createOpenMrsPatientResponse.body)); expect(createOpenMrsPatientResponse.status).toBe(201); expect(createOpenMrsPatientResponse.body).toHaveProperty('id'); @@ -213,8 +208,14 @@ describe('Workflows', () => { .auth(FHIR.username, FHIR.password); expect(retrieveFhirPatientIdResponse.status).toBe(200); expect(retrieveFhirPatientIdResponse.body.total).toBe(1); + + const chtPatientId = retrieveFhirPatientIdResponse.body.entry[0].resource.identifier[1].value; + + const retrieveChtPatientResponse = await request(CHT.url) + .get('/api/v1/person/' + chtPatientId) + .auth(CHT.username, CHT.password); + expect(retrieveChtPatientResponse.status).toBe(200); }); - }); describe('Loss To Follow-Up (LTFU) workflow', () => { let encounterUrl: string; From c8638b70793ec4d78cb9692f535f1b4db63c348f Mon Sep 17 00:00:00 2001 From: Maria Lorena Rodriguez Viruel Date: Thu, 5 Dec 2024 10:16:16 -0300 Subject: [PATCH 4/4] fix(#123): improve cht hierarchy --- mediator/test/cht-resource-factories.ts | 20 +++++++------ mediator/test/workflows.spec.ts | 38 +++++++++++++++++-------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/mediator/test/cht-resource-factories.ts b/mediator/test/cht-resource-factories.ts index cd3830b1..53036b96 100644 --- a/mediator/test/cht-resource-factories.ts +++ b/mediator/test/cht-resource-factories.ts @@ -1,11 +1,11 @@ import { randomUUID } from 'crypto'; import { Factory } from 'rosie'; -const PlaceFactory = Factory.define('place') +export const PlaceFactory = Factory.define('place') .option('placeId', randomUUID()) .attr('name', 'CHP Branch One') .attr('type', 'district_hospital') - .attr('parent', ['placeId'], function (placeId) { + .attr('placeId', ['placeId'], function (placeId) { return placeId; }); @@ -14,19 +14,23 @@ const ContactFactory = Factory.define('contact') .attr('phone', '+2868917046'); export const UserFactory = Factory.define('user') - .option('placeId') + .option('parentPlace') .attr('password', 'Dakar1234') .attr('username', 'maria') .attr('type', 'chw') - .attr('place', ['placeId'], function (placeId) { - return PlaceFactory.build({}, { placeId }); + .attr('place', ['parentPlace'], function (parentPlace) { + return { + "name": "Mary's Area", + "type": "health_center", + "parent": parentPlace + }; }) .attr('contact', function () { return ContactFactory.build(); }); export const PatientFactory = Factory.define('patient') - .option('placeId') + .option('place') .attr('name', 'John Test') .attr('phone', '+2548277217095') .attr('date_of_birth', '1980-06-06') @@ -34,8 +38,8 @@ export const PatientFactory = Factory.define('patient') .attr('type', 'person') .attr('role', 'patient') .attr('contact_type', 'patient') - .attr('place', ['placeId'], function (placeId) { - return placeId; + .attr('place', ['place'], function (place) { + return place; }); const DocsFieldsFactory = Factory.define('fields') diff --git a/mediator/test/workflows.spec.ts b/mediator/test/workflows.spec.ts index e2b9ed78..f5dba817 100644 --- a/mediator/test/workflows.spec.ts +++ b/mediator/test/workflows.spec.ts @@ -1,7 +1,7 @@ import request from 'supertest'; import { OPENHIM, CHT, FHIR, OPENMRS } from '../config'; import { - UserFactory, PatientFactory, TaskReportFactory + UserFactory, PatientFactory, TaskReportFactory, PlaceFactory } from './cht-resource-factories'; import { EndpointFactory as EndpointFactoryBase, @@ -70,26 +70,28 @@ const createOpenMRSIdType = async (name: string) => { } }; -let placeId: string; +const parentPlace = PlaceFactory.build(); let chwUserName: string; let chwPassword: string; let contactId: string; let patientId: string; +let parentPlaceId: string; +let placeId: string; + const configureCHT = async () => { const createPlaceResponse = await request(CHT.url) .post('/api/v1/places') .auth(CHT.username, CHT.password) - .send({ 'name': 'CHP Branch Two', 'type': 'district_hospital' }); + .send(parentPlace); if (createPlaceResponse.status === 200 && createPlaceResponse.body.ok === true) { - placeId = createPlaceResponse.body.id; + parentPlaceId = createPlaceResponse.body.id; } else { throw new Error(`CHT place creation failed: Reason ${createPlaceResponse.status}`); } - const user = UserFactory.build({}, { placeId: placeId }); - + const user = UserFactory.build({}, { parentPlace: parentPlaceId }); chwUserName = user.username; chwPassword = user.password; @@ -102,6 +104,15 @@ const configureCHT = async () => { } else { throw new Error(`CHT user creation failed: Reason ${createUserResponse.status}`); } + + const retrieveChtHealthCenterResponse = await request(CHT.url) + .get('/api/v2/users/maria') + .auth(CHT.username, CHT.password); + if (retrieveChtHealthCenterResponse.status === 200) { + placeId = retrieveChtHealthCenterResponse.body.place[0]._id; + } else { + throw new Error(`CHT health center retrieval failed: Reason ${retrieveChtHealthCenterResponse.status}`); + } }; describe('Workflows', () => { @@ -122,12 +133,13 @@ describe('Workflows', () => { expect(checkMediatorResponse.status).toBe(200); expect(checkMediatorResponse.body.status).toBe('success'); - const patient = PatientFactory.build({name: 'OpenMRS Patient', phone: '+2548277217095'}, { placeId: placeId }); + const patient = PatientFactory.build({name: 'CHTOpenMRS Patient', phone: '+2548277217095'}, { place: placeId }); const createPatientResponse = await request(CHT.url) .post('/api/v1/people') .auth(chwUserName, chwPassword) .send(patient); + expect(createPatientResponse.status).toBe(200); expect(createPatientResponse.body.ok).toEqual(true); patientId = createPatientResponse.body.id; @@ -168,7 +180,7 @@ describe('Workflows', () => { ); const searchOpenMrsPatientResponse = await request(OPENMRS.url) - .get(`/Patient/?given=OpenMRS&family=Patient`) + .get(`/Patient/?given=CHTOpenMRS&family=Patient`) .auth(OPENMRS.username, OPENMRS.password); expect(searchOpenMrsPatientResponse.status).toBe(200); expect(searchOpenMrsPatientResponse.body.total).toBe(1); @@ -181,7 +193,7 @@ describe('Workflows', () => { .auth(FHIR.username, FHIR.password); expect(checkMediatorResponse.status).toBe(200); - const openMrsPatient = OpenMRSPatientFactory.build({}, {placeId}); + const openMrsPatient = OpenMRSPatientFactory.build({}, {placeId: parentPlace.placeId}); const createOpenMrsPatientResponse = await request(OPENMRS.url) .post('/Patient') @@ -201,7 +213,7 @@ describe('Workflows', () => { .send(); expect(triggerOpenMrsSyncPatientResponse.status).toBe(200); - await new Promise((r) => setTimeout(r, 10000)); + await new Promise((r) => setTimeout(r, 20000)); const retrieveFhirPatientIdResponse = await request(FHIR.url) .get('/fhir/Patient/?identifier=' + openMrsPatientId) @@ -217,6 +229,8 @@ describe('Workflows', () => { expect(retrieveChtPatientResponse.status).toBe(200); }); + }); + describe('Loss To Follow-Up (LTFU) workflow', () => { let encounterUrl: string; let endpointId: string; @@ -263,7 +277,7 @@ describe('Workflows', () => { expect(retrieveOrganizationResponse.status).toBe(200); expect(retrieveOrganizationResponse.body.total).toBe(1); - const patient = PatientFactory.build({}, { name: 'LTFU patient', placeId: placeId }); + const patient = PatientFactory.build({}, { name: 'LTFU patient', place: placeId }); const createPatientResponse = await request(CHT.url) .post('/api/v1/people') @@ -294,7 +308,7 @@ describe('Workflows', () => { expect(sendMediatorServiceRequestResponse.status).toBe(201); encounterUrl = sendMediatorServiceRequestResponse.body.criteria; - const taskReport = TaskReportFactory.build({}, { placeId, contactId, patientId }); + const taskReport = TaskReportFactory.build({}, { placeId: placeId, contactId, patientId }); const submitChtTaskResponse = await request(CHT.url) .post('/medic/_bulk_docs')