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

feat(#114): Create OpenMRS Mediator #115

Open
wants to merge 69 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
5e4451f
feat(#114): Create OpenMRS Mediator for Patient resource
witash Mar 14, 2024
4e3de0f
feat(#114): add endpoints for openmrs and cht
witash Apr 7, 2024
534b1fd
feat(#114): add missing routes
witash Apr 8, 2024
50a3f3f
feat(#114): add openmrs poller
witash Apr 16, 2024
494ae3d
feat(#114): id exchange and sms forms
witash Apr 22, 2024
8c3c4fe
feat(#114): add patient to bundle from openmrs
witash Apr 23, 2024
6ed106d
feat(#114): allow palce id in name
witash Apr 29, 2024
76e9012
feat(#124): remove openmrs endpoints, add mappers for cht, openmrs
witash May 20, 2024
ffaa7e0
feat(#124): add openmrs sync
witash May 20, 2024
9b7978d
feat(#114): add sequence diagrams for document endpoints
witash May 21, 2024
ac83b03
feat(#125): sequence diagrams for outgoing patients
witash May 21, 2024
1fb1351
feat(#114): fix uit tests
witash May 22, 2024
d9e1c13
feat(#125): sequence diagrams for incoming patients and forms
witash May 23, 2024
449de72
feat(#114): sonar fixes
witash May 24, 2024
613bf6b
feat(#114): add openmrs channel to configurator
witash May 24, 2024
6d03a4f
feat(#114): fix bundle format
witash May 27, 2024
aba7c95
feat(#114): get observations and patients together with encounters
witash May 31, 2024
fda229f
feat(#114): configurator changes
witash May 31, 2024
f7a834d
feat(#114): add openmrs to startup
witash May 31, 2024
dfe749d
feat(#114): add timeout, fix timing issues
witash Jun 4, 2024
6c3ff27
feat(#114): pagination for openmrs sync
witash Jun 6, 2024
3eab817
feat(#114): fix reference for encounters and don't allow updates
witash Jun 10, 2024
0e38c83
feat(#114): fix unit tests
witash Jun 10, 2024
d05968e
feat(#114): fix unit tests
witash Jun 10, 2024
70aee07
feat(#114): remove cht from main startup script
witash Jun 11, 2024
6141ce8
feat(#114): adding source ot prevent infinite loops
witash Jun 21, 2024
5bb2eae
feat(#114): don't save encounters until complete
witash Jun 21, 2024
12e2a53
feat(#114): boundary conditions for sync
witash Jun 24, 2024
88f1014
feat(#114): add sync_interval to index
witash Jun 24, 2024
7a5243c
feat(#114): fix tests
witash Jun 24, 2024
3db5945
feat(#114): fix defualt sync_interval
witash Jun 24, 2024
82e29ec
feat(#114): fix unit tests for default sync_interval
witash Sep 9, 2024
ee8e8dd
feat: add platform to failing containers
njuguna-n Sep 18, 2024
6d2c7d2
no service line in compose files, pin to cht core 4.10, improve start…
mrjones-plip Sep 18, 2024
e91be25
fix(#138): change ltfu mediator to cht mediator and add openmrs mediator
witash Oct 2, 2024
c1cc023
fix(#138): env.template changes and small fixes
witash Oct 3, 2024
f325edd
chore (#123): openmrs mediator e2e test (#128)
lorerod Oct 4, 2024
7470359
fix(#123): fixing tests
witash Oct 8, 2024
a0fc96b
fix(#123): fixing tests
witash Oct 8, 2024
86bdc31
chore(#142): adding tests to increase coverage
witash Oct 18, 2024
59f607b
Merge branch 'main' into openmrs-mediator
witash Oct 21, 2024
1a5be52
chore(#142): sonar fixes
witash Oct 22, 2024
d6db1d6
chore(#142): sonar fixes
witash Oct 22, 2024
7d279c1
chore(#142): adding tests to increase coverage (#143)
witash Oct 23, 2024
d5b0252
fix: fixing startup script
witash Oct 23, 2024
ef558a2
chore(#136): skipping e2e-tests until rate limiting is fixed
witash Oct 23, 2024
d8cb295
feat(#138): move polling to openhim channel config (#139)
witash Oct 25, 2024
e3f1a4c
chore(#142): add tests and small fixes
witash Oct 28, 2024
dfe2107
Merge branch '142-unit-tests' into openmrs-mediator
witash Oct 28, 2024
59bf494
fix(#123): use up-test command in retry_startup
Oct 31, 2024
0f7ef52
fix(#123): remove conditional to enable E2E tests in CI
Oct 31, 2024
61ccd62
fix(#123): remove conditional to enable E2E tests in CI
Oct 31, 2024
5be2b00
fix: convert sync period to milliseconds before subtracting from the …
njuguna-n Nov 1, 2024
8abfa10
fix(#123): remove conditional to enable E2E tests in CI
Nov 1, 2024
3cc6a55
fix(#123): revert remove conditional to enable E2E tests in CI
Nov 1, 2024
3216ce7
fix(#123): add wait time before asserting fhir response
Nov 6, 2024
71df8de
fix(#123): add logic to retry image pulls
Nov 6, 2024
1ec5181
fix(#123): enable e2e test in ci
Nov 6, 2024
670f3a5
feat: make encounter requests idempotent by using the identifier
njuguna-n Nov 8, 2024
0bca7ae
feat: get fhir resource by identifier
njuguna-n Nov 11, 2024
dcb5e61
feat(#147): adding value types (#149)
witash Nov 13, 2024
40716e8
chore: adding sample forms
witash Nov 19, 2024
1de75e4
fix: removing date check
witash Nov 19, 2024
c9354ff
chore: address sonarlint issues
njuguna-n Nov 25, 2024
7b89788
chore: remove unused variable
andrablaj Nov 28, 2024
f361ca4
fix: adding postman collection
witash Jan 9, 2025
9290c0f
fix: app setting changes for testing
witash Jan 10, 2025
85d0484
Merge branch 'main' into openmrs-mediator
witash Jan 10, 2025
539f03e
feat: separating test and openmrs job
witash Jan 10, 2025
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
3 changes: 3 additions & 0 deletions docker/docker-compose.mediator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ services:
- "FHIR_URL=${FHIR_URL:-http://openhim-core:5001/fhir}"
- "FHIR_USERNAME=${FHIR_USERNAME:-interop-client}"
- "FHIR_PASSWORD=${FHIR_PASSWORD:-interop-password}"
- "OPENMRS_URL=${OPENMRS_URL:-http://openhim-core:5001/openmrs}"
- "OPENMRS_USERNAME=${OPENMRS_USERNAME:-interop-client}"
- "OPENMRS_PASSWORD=${OPENMRS_PASSWORD:-interop-password}"
- "CHT_URL=${CHT_URL:-https://nginx}"
- "CHT_USERNAME=${CHT_USERNAME:-admin}"
- "CHT_PASSWORD=${CHT_PASSWORD:-password}"
Expand Down
15 changes: 15 additions & 0 deletions docker/docker-compose.openmrs-poller.yml
witash marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: '3.9'

services:
openmrs-poller:
build: ../openmrs-poller
container_name: openmrs-poller
networks:
- cht-net
environment:
- OPENMRS_URL=${OPENMRS_URL}
- OPENMRS_USER=${OPENMRS_USER}
- OPENMRS_PASSWORD=${OPENMRS_PASSWORD}
- OPENHIM_URL=${OPENHIM_URL}
- OPENHIM_USER=${OPENHIM_USER}
- OPENHIM_PASSWORD=${OPENHIM_PASSWORD}
Binary file added docs/sequence-diagram/cht-form-submission.png
Copy link
Contributor

Choose a reason for hiding this comment

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

@witash, can you please add the configs to cht-config so that we can reproduce the entire workflow in the e2e tests using only this repo and not depending on config-gandaki?
Let me know if I can help.
Thanks

Copy link
Member

Choose a reason for hiding this comment

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

Should the config be private @binokaryg, as is the case with most of our projects?

@lorerod is there an alternative to testing in case the config cannot be copied in a public repo?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, the config is private with limited access to collaborators.

Copy link
Contributor

Choose a reason for hiding this comment

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

@andrablaj, from what I understand, we do not need the entire config. It's just an outbound push configuration and a couple of forms that do not need to be precisely the same. I’m right, @witash?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yea, for the LTFU the configurator put the outbound push configuration in the settings automatically.
What we need to add/modify for tests is not anything specific to the gandaki config. a sample outbound push for cht form submission and inbound form for patient creation and inblound forms

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added the inbound forms and outbound for patient creation

Copy link
Contributor

Choose a reason for hiding this comment

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

So, with this, can I create a patient from CHT to Openmrs and vice versa?

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions docs/sequence-diagram/cht-form-submission.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
title CHT Form Submission

participant CHT

participantgroup
participant OpenHIM
participant Mediator
participant FHIR Server
end

participant Requesting System
autoactivation on
box over CHT: Form submission in CHT
box over CHT: Outbound push\nrecognizes a\ntracked form was submitted
CHT->OpenHIM: Outbound push POSTS Data Record\nDocument to OpenHim
OpenHIM->Mediator:
box over Mediator: Mediator creates Encounter FHIR
box over Mediator: Mediator creates Observation FHIR Resources\nfrom the fields in the data record document
box over Mediator: Mediator creates Bundle\nfrom Encounter and Observations
Mediator->FHIR Server: POST Encounter Bundle
box over FHIR Server: FHIR Server saves\nthe resources in the Bundle
FHIR Server-->Mediator: FHIR Bundle Response
Mediator-->OpenHIM: 200 Response (no body)
OpenHIM-->CHT: 200 Response (no body)
destroysilent CHT
box over Requesting System: Any FHIR Compliant Server\nthat has been set up as a client\nin OpenHIM can now request\nEncounter or Observation records from CHT
Requesting System->OpenHIM: GET FHIR Encounter
OpenHIM->FHIR Server: GET FHIR Encounter
FHIR Server-->OpenHIM: FHIR Searchset response
OpenHIM-->Requesting System: FHIR Searchset Response
autoactivation off
Binary file added docs/sequence-diagram/cht-incoming-forms.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions docs/sequence-diagram/cht-incoming-forms.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
title CHT Incoming Forms

participant Requesting System

participantgroup
participant OpenHIM
participant Mediator
participant FHIR Server
end

participant CHT

autoactivation on
box over Requesting System: A FHIR Encounter\nwith Observations is created
activate Mediator
Mediator->Requesting System: Mediator GETs FHIR Encounters
Requesting System-->Mediator: FHIR Searchset Response
Mediator->Requesting System: Mediator GETs FHIR Observations
Requesting System-->Mediator: FHIR Searchset Response
Mediator->FHIR Server: Mediator GETs FHIR Encounters
FHIR Server-->Mediator: FHIR Searchset Response
Mediator->FHIR Server: Mediator GETs FHIR Observations
FHIR Server-->Mediator: FHIR Searchset Response
box over Mediator: Mediator compares Resources from\nFHIR Server and Requesting System
box over Mediator: Encounters found in Requesting System but\nnot in Requesting System are sent to CHT
box over Mediator: Mediator converts FHIR Observations\n for each Encounter into form fields
Mediator->CHT: Mediator POSTS to the records API
box over CHT: CHT saves a data_record\nwith the data from the patient\ncreation form
CHT-->Mediator: CHT Responds with source record id.
deactivate Mediator
autoactivation off

Binary file added docs/sequence-diagram/cht-incoming-patients.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions docs/sequence-diagram/cht-incoming-patients.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
title CHT Incoming Patients

participant Requesting System

participantgroup
participant OpenHIM
participant Mediator
participant FHIR Server
end

participant CHT

autoactivation on
box over Requesting System: A FHIR Patient is created
Mediator->Requesting System: Mediator GETs FHIR Patients
activate Mediator
Requesting System-->Mediator: FHIR Searchset Response
Mediator->FHIR Server: Mediator GETs FHIR Patients
FHIR Server-->Mediator: FHIR Searchset Response
box over Mediator: Mediator compares Resources from\nFHIR Server and Requesting System
box over Mediator: Patients found in FHIR Serverbut\nnot in Requesting System are sent to Requesting System
Mediator->CHT: Mediator POSTS to the records API
box over CHT: CHT saves a data_record\nwith the data from the patient\ncreation form
CHT-->Mediator: CHT Responds with source record id.
deactivate Mediator
box over CHT: CHT creates a patient document
box over CHT: CHT adds parent using place_id
box over CHT: CHT adds patient_id and other fields with transitions
CHT->OpenHIM: Outbound push recognizes new patient and sends
OpenHIM->Mediator:
Mediator->FHIR Server: GET FHIR Patient\nusing external_id
FHIR Server-->Mediator: FHIR Patient
box over Mediator: Mediator adds CHT and Medic ID
Mediator->FHIR Server: PUT FHIR Patient\nwith updated ids
FHIR Server-->Mediator:
Mediator-->OpenHIM:
OpenHIM-->CHT:
autoactivation off
Binary file added docs/sequence-diagram/cht-outgoing-patients.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions docs/sequence-diagram/cht-outgoing-patients.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
title CHT Outgoing Patients

participant Requesting System

participantgroup
participant OpenHIM
participant Mediator
participant FHIR Server
end

autoactivation on
box over Requesting System: A FHIR Patient is created
Mediator->Requesting System: Mediator GETs FHIR Patients
activate Mediator
Requesting System-->Mediator: FHIR Searchset Response
Mediator->FHIR Server: Mediator GETs FHIR Patients
FHIR Server-->Mediator: FHIR Searchset Response
box over Mediator: Mediator compares Resources from\nFHIR Server and Requesting System
box over Mediator: Patients found in FHIR Serverbut\nnot in Requesting System are sent to Requesting System
Mediator->Requesting System: Mediator POSTS new Patients
Requesting System-->Mediator: FHIR Patient Response\n(w/ Requesting System ids)
Mediator->FHIR Server: Mediator PUTS Patient\nw/ updated ids from requesting System
FHIR Server-->Mediator:
deactivate Mediator
autoactivation off
Binary file added docs/sequence-diagram/cht-patient-creation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions docs/sequence-diagram/cht-patient-creation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
title CHT Patient Creation

participant CHT

participantgroup
participant OpenHIM
participant Mediator
participant FHIR Server
end

participant Requesting System
autoactivation on
box over CHT: Patient is created in CHT
box over CHT: Outbound push\nrecognizes changes
CHT->OpenHIM: Outbound push POSTS Patient\nDocument to OpenHim
OpenHIM->Mediator:
box over Mediator: Mediator converts Patient\nDocument to FHIR Patient
Mediator->FHIR Server: POST Patient FHIR
box over FHIR Server: FHIR Server saves\nthe Patient FHIR
FHIR Server-->Mediator: FHIR Patient Response
Mediator-->OpenHIM: 200 Response (no body)
OpenHIM-->CHT: 200 Response (no body)
destroysilent CHT
box over Requesting System: Any FHIR Compliant Server\nthat has been set up as a client\nin OpenHIM can now request\nPatient records from CHT
Requesting System->OpenHIM: GET FHIR Patient
OpenHIM->FHIR Server: GET FHIR Patient
FHIR Server-->OpenHIM: FHIR Searchset response
OpenHIM-->Requesting System: FHIR Searchset Response
autoactivation off
5 changes: 5 additions & 0 deletions mediator/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export const CHT = {
password: getEnvironmentVariable('CHT_PASSWORD', 'password'),
};

export const OPENMRS = {
url: getEnvironmentVariable('OPENMRS_URL', 'http://openhim-core:5001/openmrs'),
username: getEnvironmentVariable('OPENMRS_USERNAME', 'interop-client'),
password: getEnvironmentVariable('OPENMRS_PASSWORD', 'interop-password'),
};

function getEnvironmentVariable(env: string, def: string) {
if (process.env.NODE_ENV === 'test') {
Expand Down
5 changes: 5 additions & 0 deletions mediator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import serviceRequestRoutes from './src/routes/service-request';
import encounterRoutes from './src/routes/encounter';
import organizationRoutes from './src/routes/organization';
import endpointRoutes from './src/routes/endpoint';
import chtRoutes from './src/routes/cht';
import { registerMediatorCallback } from './src/utils/openhim';
import os from 'os';

Expand All @@ -24,12 +25,16 @@ app.get('*', (_: Request, res: Response) => {
res.send({status: 'success', osuptime: osUptime, processuptime: processUptime});
});

// routes for valid fhir resources
app.use('/patient', patientRoutes);
app.use('/service-request', serviceRequestRoutes);
app.use('/encounter', encounterRoutes);
app.use('/organization', organizationRoutes);
app.use('/endpoint', endpointRoutes);

// routes for cht docs
app.use('/cht', chtRoutes);

if (process.env.NODE_ENV !== 'test') {
app.listen(PORT, () => logger.info(`Server listening on port ${PORT}`));

Expand Down
65 changes: 65 additions & 0 deletions mediator/src/controllers/cht.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
createFhirResource,
updateFhirResource,
getFHIRPatientResource,
addId
} from '../utils/fhir';
import {
buildFhirObservationFromCht,
buildFhirEncounterFromCht,
buildFhirPatientFromCht,
chtPatientIdentifierType,
chtDocumentIdentifierType
} from '../mappers/cht';
import { getPatientUUIDFromSourceId } from '../utils/cht';

export async function createPatient(chtPatientDoc: any) {
// hack for sms forms: if source_id but not _id,
// first get patient id from source
if (chtPatientDoc.doc.source_id){
chtPatientDoc.doc._id = await getPatientUUIDFromSourceId(chtPatientDoc.source_id);
}

const fhirPatient = buildFhirPatientFromCht(chtPatientDoc.doc);
// create or update in the FHIR Server
// note that either way, its a PUT with the id from the patient doc
return updateFhirResource({ ...fhirPatient, resourceType: 'Patient' });
}

export async function updatePatientIds(chtFormDoc: any) {
// first, get the existing patient from fhir server
const response = await getFHIRPatientResource(chtFormDoc.openmrs_patient_uuid);

if (response.status != 200) {
return { status: 500, data: { message: `FHIR responded with ${response.status}`} };
} else if (response.data.total == 0){
return { status: 404, data: { message: `Patient not found`} };
}

const fhirPatient = response.data.entry[0].resource;
addId(fhirPatient, chtPatientIdentifierType, chtFormDoc.patient_id);

// now, we need to get the actual patient doc from cht...
const patient_uuid = await getPatientUUIDFromSourceId(chtFormDoc._id);
addId(fhirPatient, chtDocumentIdentifierType, patient_uuid);

return updateFhirResource({ ...fhirPatient, resourceType: 'Patient' });
}

export async function createEncounter(chtReport: any) {
const fhirEncounter = buildFhirEncounterFromCht(chtReport);

const bundle: fhir4.Bundle = {
resourceType: 'Bundle',
type: 'collection',
entry: [fhirEncounter]
}

for (const entry of chtReport.observations) {
if (entry.valueCode || entry.valueString || entry.valueDateTime) {
const observation = buildFhirObservationFromCht(chtReport.patient_uuid, fhirEncounter, entry);
bundle.entry?.push(observation);
}
}
return createFhirResource(bundle);
}
Loading
Loading