Skip to content

Fix HIE integration: per-instance pipelines, OpenCR matching, module patches, and facility identification#3

Merged
ibacher merged 52 commits into
charess-org:mainfrom
mherman22:fix/pipeline-shr-integration
Apr 9, 2026
Merged

Fix HIE integration: per-instance pipelines, OpenCR matching, module patches, and facility identification#3
ibacher merged 52 commits into
charess-org:mainfrom
mherman22:fix/pipeline-shr-integration

Conversation

@mherman22

@mherman22 mherman22 commented Mar 20, 2026

Copy link
Copy Markdown

Summary

This PR fixes the end-to-end data flow across the Sedish Haiti HIE — from iSantePlus EMR instances through OpenHIM, the SHR mediator, HAPI FHIR, and the OpenCR Client Registry. The goal is to enable the roaming care workflow: a patient registered at one facility can be identified and their full record retrieved at any other facility via the golden record in the Client Registry.


FHIR Data Pipeline — per-instance architecture

Each iSantePlus instance now gets its own dedicated pipeline instead of routing through a shared FHIR aggregator mediator.

Files changed:

  • `packages/data-pipeline-isanteplus/config/application.yaml` → renamed to `application-isanteplus1.yaml`, points at `http://isanteplus:8080/openmrs/ws/fhir2/R4\`
  • `packages/data-pipeline-isanteplus/config/application-isanteplus2.yaml` — new, points at `http://isanteplus2:8080/openmrs/ws/fhir2/R4\`
  • `packages/data-pipeline-isanteplus/docker-compose.yml` — two services (`pipeline-isanteplus1`, `pipeline-isanteplus2`), each with its own DWH volume
  • `config.yaml` — removed `fhir-aggregator-mediator` package
  • Deleted `packages/fhir-aggregator-mediator/` entirely

Decision: Pipelines are staggered (pipeline 1 at :00, pipeline 2 at :30) to avoid 409 Conflict errors from shared Practitioner/Location UUIDs across cloned databases.

Sink credentials: Changed from `root@openhim.org` to `shr-pipeline` with `instant101`.

Resource order: `Practitioner,Location,Patient,Encounter,Observation,...` — referenced resources must sync before resources that reference them to satisfy HAPI FHIR's referential integrity enforcement.


iSantePlus EMR

Files changed:

  • `packages/emr-isanteplus/Dockerfile` — custom image with `post-start.sh`, fixes startup.sh to wait for Tomcat PID instead of post-start.sh
  • `packages/emr-isanteplus/config/post-start.sh` — new script that runs after OpenMRS boots and sets per-facility xds-sender endpoints, OpenHIM credentials (`xdssender.oshr.*`), and mpi-client credentials (`mpi-client.msg.sendingApplication`, `mpi-client.security.authtoken`) to the `FACILITY_ID`
  • `packages/emr-isanteplus/docker-compose.yml` — added `FACILITY_ID` and `FACILITY_NAME` env vars per instance (`hueh`, `lapaix`)
  • `packages/emr-isanteplus/config/custom_modules/xds-sender-2.5.9.omod` — replaces `xds-sender-2.5.0-SNAPSHOT.omod`. Built from IsantePlus/openmrs-module-xds-sender#110, fixes `SSLPeerUnverifiedException` in `PullNotificationsTask` by honoring `xdssender.exportCcd.ignoreCerts` for the OkHttpClient
  • `packages/emr-isanteplus/config/custom_modules/santedb-mpiclient-1.1.4.omod` — new. Built from IsantePlus/openmrs-module-mpi-client#58, fixes `APIAuthenticationException: Privileges required: Get Identifier Types` by adding `Context.addProxyPrivilege()` in `PatientSyncWorker` and `PatientUpdateWorker`

Decision: The base image's `startup.sh` uses `wait ${!}` which waits for the last background process (post-start.sh) instead of Tomcat. When post-start.sh exits, the container dies. The Dockerfile patches this to capture `TOMCAT_PID=$!` and use `wait $TOMCAT_PID`.


OpenHIM Interoperability Layer

Files changed:

  • `packages/interoperability-layer-openhim/importer/volume/openhim-import.json` — replaced the generic `isanteplus` client with per-facility clients (`hueh`, `lapaix`, `ofatma`, etc.) each with `emr` role. Added `openshr` client with `shr` + `emr` roles for SHR mediator CR queries. All password hashes use `sha512(password + salt)` — not `sha512(salt + password)`.
  • `packages/interoperability-layer-openhim/importer/docker-compose.config.yml` — importer uses `instant101` for environments with persisted MongoDB data

Decision: Per-facility clients enable OpenCR to identify the Point of Service (which facility submitted the patient) via the `x-openhim-clientid` header. OpenCR stamps this as a `meta.tag` on the Patient resource and displays it in the CRUX UI. The password hash order (`password + salt`, not `salt + password`) matches OpenHIM's implementation — getting this wrong causes silent 401s.


Shared Health Record (SHR) Mediator

Files changed:

  • `packages/shared-health-record-fhir/config/config.json` — added `clientRegistryUsername` / `clientRegistryPassword` (`openshr`/`openshr`) for authenticated MPI lookups via OpenHIM. The SHR mediator queries OpenCR for golden records when the pipeline pushes Patient resources — without these credentials, the CR channel returns 401.
  • `packages/shared-health-record-fhir/docker-compose.yml` — uses upstream `itechuw/shared-health-record:main` image which now includes MPI resolution on write path (DIGI-UW/shared-health-record#115) and empty bundle handling (DIGI-UW/shared-health-record#119)

Client Registry (OpenCR)

Files changed:

  • `packages/client-registry-opencr/config/decisionRules.json`:
    • Expanded from 2 rules to 10 rules with Jaro-Winkler and DateDamerau fuzzy matching
    • Fixed `espath` field names: `code_national` → `codeNational` to match Elasticsearch index names. This was the root cause of OpenCR creating separate golden records for patients with the same Code National — the matching engine couldn't find candidates because the ES field name didn't match.
  • `packages/client-registry-opencr/config/config.json`:
    • Added per-facility clients (`hueh`, `lapaix`, etc.) to `clients` array for CRUX UI Submitting System / Point of Service display
    • Added `codepc` system entry with `displayName: "Code PC"` so the CRUX UI shows a label instead of the raw URI
    • Reset `lastFHIR2ESSync` to force full re-index

Decision: The `espath` mismatch was subtle — the decision rules used snake_case (`code_national`) but OpenCR's ES indexing uses camelCase (`codeNational`). This caused deterministic matching rules to silently fail. The fix was a one-character change per field but had a large impact on matching accuracy.


HAPI FHIR Datastore

Files changed:

  • `packages/fhir-datastore-hapi-fhir/docker-compose.yml` — added `reverse-proxy_public` network
  • `packages/fhir-datastore-hapi-fhir/post-deploy.sh` — new script that adds network, enables referential integrity enforcement, and sets placeholder reference targets. Must run after every deploy because the Instant CLI reads compose files from the base image, not local overrides.

Database (MySQL)

Files changed:

  • `packages/database-mysql/config/initdb/isanteplus-db.sql`:
    • Fixed `mpi-client.pid.exportIdentitiferType`: `Patient ID=iSantePlus ID` → `iSantePlus ID=iSantePlus ID` (the key must match the actual OpenMRS identifier type name)
    • Added `iSante ID` to the identifier mapping
    • Removed `ECID` from `mpi-client.pid.autoXref` (generated by the CR, not a local identifier)
    • Added Code PC FHIR system URI (`http://isanteplus.org/openmrs/fhir2/9-code-pc\`) and Biometrics National Reference Code to `fhir_patient_identifier_system` table — without this, Code PC was sent with no `system` URI, making it invisible to OpenCR
  • `projects/isanteplus-db/initdb/isanteplus-db.sql` — same changes
  • `projects/isanteplus-db/initial-db/isanteplus-db.sql` — same changes

Reverse Proxy (Nginx)

Files changed:

  • `packages/reverse-proxy-nginx/package-conf-secure/http-isanteplus-secure.conf` — added Host header and redirect rewrite
  • `packages/reverse-proxy-nginx/package-conf-secure/http-shr-secure.conf` — new, proxies SHR FHIR browser
  • Removed `http-pipeline-secure.conf` — pipelines are internal services, no external access needed

README

Complete rewrite covering architecture, deployment (both Instant CLI and manual docker stack), pipeline management from terminal, adding new instances, SSL certificates, troubleshooting, and OpenHIM password management.


Test results

  • Both per-instance pipelines complete full runs without errors (run sequentially to avoid 409 on shared UUIDs)
  • Empty bundles accepted gracefully (no more pipeline crash on 400)
  • MPI resolution: patients in SHR carry `Patient.link[type=refer]` to golden records
  • OpenCR matching: two patients with same Code National at different facilities → same golden record
  • OpenCR CRUX UI: patient names displayed, Code PC labeled, Submitting System shows facility name
  • xds-sender: no more SSL errors on PullNotificationsTask
  • mpi-client: no more privilege errors, patients sync to OpenCR on creation
  • Per-facility OpenHIM auth: each iSantePlus instance authenticates as its facility

@mherman22 mherman22 force-pushed the fix/pipeline-shr-integration branch from 6c963b8 to 49020e9 Compare March 21, 2026 19:56
@mherman22 mherman22 changed the title Fix data pipeline integration: auth, SHR image, nginx proxy, and HAPI config Fix OpenCR patient overwrite, iSantePlus restart loop, SHR mediator, and security issues Mar 22, 2026
… config

- Fix pipeline sink auth: use OpenHIM client `shr-pipeline` (with correct
  sha512 password hash) instead of API user `root@openhim.org`, which does
  not work on OpenHIM's HTTP transaction port (5001)

- Update SHR image to `main` tag which includes the fix for double-serialized
  JSON responses in the bundle POST handler
  (see DIGI-UW/shared-health-record#112)

- Add nginx reverse proxy config for the pipeline control panel, accessible
  at https://pipeline.<domain> instead of requiring port 8095 in the firewall

- Add pipeline service to reverse-proxy network so nginx can reach it

- Add HAPI FHIR post-deploy script to set `enforce_referential_integrity_on_write=false`
  and `auto_create_placeholder_reference_targets=true` — required because the
  pipeline pushes resources by type (not in dependency order) and the instant
  tooling does not apply dotted env vars from docker-compose.yml

- Update README with Step 10b and Known Gotchas entry for the HAPI FHIR override
- Rewrite README.md as a single professional document with clear
  architecture diagram, data flow table, quick start guide,
  component reference, troubleshooting section, and security notes

- Remove SETUP-GUIDE.md (content consolidated into README)

- Add openhim_public network to all iSantePlus instances in
  docker-compose.yml so the mpi-client module can reach
  openhim-core:5001 to push patient identities to OpenCR
…ting

The pipeline starts faster than iSantePlus (~10s vs ~10min). Without
this fix, the first run hits a non-FHIR response, creates a corrupt
DWH baseline, and all subsequent incremental runs find no new data.

The wrapper script (wait-and-start.sh):
1. Polls the FHIR source /metadata endpoint every 10s (up to 10min)
2. Clears any stale DWH so the first run is always a full sync
3. Hands off to the original entrypoint

This eliminates the need to manually trigger a full run after deployment.
…nfig

Security:
- Restrict SHR nginx proxy to GET/HEAD only (writes must go through OpenHIM)
- Change OpenHIM /xds-order-notification channel from public to private auth
- Remove DELETE method from OpenCR channel
- Remove allow_multiple_delete from HAPI FHIR overrides
- Remove exposed pipeline management ports (8095-8098)

Reliability:
- Add deploy section with restart_policy and memory limits to all 4 pipelines
- Fix wait-and-start.sh to exit with error (not proceed) when FHIR source
  times out, so Docker Swarm restart policy handles retries
- Fix wait-and-start.sh to read DWH prefix from env var override (was always
  clearing pipeline_DWH regardless of instance, breaking pipelines 2-4)
- Stagger pipeline schedules (offsets 0/1/2/3 min) to avoid thundering herd
- Reduce HikariCP pool size from 250 to 80

Per-instance iSantePlus configuration:
- Add post-start.sh that runs after OpenMRS boots to set per-instance
  global properties (mpi-client.pid.local, xds-sender endpoints, password)
- Inject post-start.sh into startup.sh via Dockerfile
- Add ISANTEPLUS_INSTANCE env var per service in docker-compose.yml
- This permanently fixes the OpenCR patient overwrite bug where all instances
  shared the same identifier URI
Without this, the SHR nginx proxy (shr.<domain>) gets a 502 because
nginx cannot reach hapi-fhir_hapi-fhir on the reverse-proxy_public
network. Previously this was applied via docker service update but
was lost on every redeploy.
The previous Dockerfile injected `post-start.sh &` before `wait ${!}`.
Since ${!} captures the PID of the last background process, it waited
for post-start.sh (which exits after configuring properties) instead
of Tomcat. This caused all 4 iSantePlus containers to exit every ~7 min.

Fix: capture Tomcat's PID immediately after launching it (TOMCAT_PID=$!)
and wait on that specific PID instead of ${!}.

Also updates README with comprehensive redeployment procedures,
expected errors table, and post-deploy verification checklist.
Documents the 6-step process: MySQL database, OpenHIM client,
docker-compose service, nginx proxy, data pipeline, and .env config.
Explains why each step is needed (OpenCR patient identity matching)
and includes the password hash generation command.
Root cause: all iSantePlus databases shared the same idgen sequence
(next_sequence_value = 810022), so all instances generated identical
patient IDs (e.g., 1000NG for the first patient). OpenCR matched
patients by iSantePlus ID and merged them into one record.

Fix: offset each database's idgen sequence by 1,000,000 per instance
(openmrs=1M, openmrs2=2M, openmrs3=3M, openmrs4=4M) so IDs never
collide across instances.
@mherman22 mherman22 force-pushed the fix/pipeline-shr-integration branch from 7b746a1 to 4c53178 Compare March 22, 2026 22:25
@mherman22 mherman22 changed the title Fix OpenCR patient overwrite, iSantePlus restart loop, SHR mediator, and security issues Fix iSantePlus restart loop, SHR mediator, xds-sender endpoints, and security issues Mar 23, 2026
@mherman22 mherman22 force-pushed the fix/pipeline-shr-integration branch from 90707b9 to 4c53178 Compare March 23, 2026 12:47
Root cause: all iSantePlus databases shared the same idgen sequence,
generating identical patient IDs (e.g., 1000NG) across all instances.
OpenCR matched on these IDs and merged patients from different facilities.

Fix: use the native OpenMRS idgen LocationBasedPrefixProvider to generate
facility-specific patient IDs (e.g., HUEH1000NG, LAPAIX1000NG).

Changes to SQL dump (isanteplus-db.sql):
- idgen_seq_id_gen.prefix set to 'provider:LocationBasedPrefixProvider'
- Added 'Facility ID Prefix' location attribute type (ID 12)
- Added 'idgen.prefixLocationAttributeType' global property

Changes to MySQL Dockerfile:
- Now copies 20-configure-per-instance.sh into the image (was missing)

Changes to 20-configure-per-instance.sh:
- Simplified to only fix xds-sender endpoints (idgen now handled in dump)

Changes to post-start.sh:
- Sets the 'Facility ID Prefix' location attribute on the default location
  using the FACILITY_NAME env var (e.g., HUEH)
- Simplified to remove multi-instance config that's no longer needed

Changes to docker-compose.yml:
- Added FACILITY_NAME=HUEH env var to isanteplus service

References:
- https://github.com/openmrs/openmrs-module-idgen/blob/master/api/src/main/java/org/openmrs/module/idgen/prefixprovider/LocationBasedPrefixProvider.java
- Container doesn't have python3 — use grep/sed for JSON parsing
- Location API rejects v=ref parameter — removed it
- Now correctly finds location UUID and sets Facility ID Prefix attribute
Replaced stale ISANTEPLUS_INSTANCE with FACILITY_NAME which drives
the idgen LocationBasedPrefixProvider for facility-specific patient IDs:
- isanteplus: HUEH
- isanteplus2: LAPAIX
- isanteplus3: OFATMA
- isanteplus4: FSC
The LocationBasedPrefixProvider approach doesn't work with idgen 4.4.1
and prefixes with characters outside the Luhn Mod-30 set (no B,I,O,Q,S,Z)
caused "Select a preferred identifier" errors on patient creation.

New approach: each facility DB gets a unique idgen sequence offset
(100k, 200k, 300k, ...) and a unique mpi-client.pid.local FHIR system
URI so OpenCR distinguishes patient sources without needing ID prefixes.

Changes:
- Remove FACILITY_NAME env var from docker-compose and post-start.sh
- Remove LocationBasedPrefixProvider, Facility ID Prefix attribute type,
  and idgen.prefixLocationAttributeType from SQL dump
- Add sequence offset + unique mpi-client.pid.local per instance in
  20-configure-per-instance.sh
- Rewrite README with comprehensive troubleshooting guide covering SSL
  certs, iSantePlus boot issues, idgen config, OpenCR deduplication,
  and common operational procedures
…ve DirectRunner workaround

Changed from:
- DirectRunner (single-threaded workaround for credential serialization)
- PostgreSQL JDBC sink

To:
- FlinkRunner (default, parallel processing)
- HTTP FHIR-Search SOURCE with proper credentials
- Source credentials: admin/Admin123 to iSantePlus FHIR API
- Sink: Keep PostgreSQL JDBC to avoid sink-side credential issues

Using volume mounts in docker-compose to ensure config changes are reflected.
Using HTTP sink for now (not PostgreSQL JDBC) to test if source credentials work.
The instant CLI is providing these sink values, so align our config file with
the deployed reality.

Focus: Test if SOURCE HTTP credentials (admin/Admin123 to iSantePlus) work
when using FlinkRunner, since openelis-openmrs-hie reports no serialization issues.
@mherman22 mherman22 changed the title Fix iSantePlus restart loop, SHR mediator, xds-sender endpoints, and security issues Fix iSantePlus restart loop, SHR mediator, xds-sender, security, and FHIR data pipeline Mar 24, 2026
@mherman22 mherman22 force-pushed the fix/pipeline-shr-integration branch 2 times, most recently from 5acef10 to 5b83683 Compare March 24, 2026 13:45
…nal image

- Fix sink username: shr-pipeline (not root@openhim.org) per openhim-import.json
- Add Location to resourceList to prevent referential integrity errors
- Add hapi-postgres-config mount to docker-compose configs
- Use original upstream fhir-data-pipes image (FlinkRunner works correctly)
- Clean up stale comments about serialization workarounds
@mherman22 mherman22 force-pushed the fix/pipeline-shr-integration branch from 5b83683 to deeca1f Compare March 24, 2026 13:51
Adds fhir-aggregator-mediator as an Instant OpenHIE package that fans
out FHIR search requests to all iSantePlus instances, merges and
deduplicates results, and presents a single endpoint to the pipeline.

Package contents:
- docker-compose.yml: Docker Swarm service with config mounts
- swarm.sh: Instant CLI lifecycle (init/up/down/destroy)
- package-metadata.json: Package descriptor with dependencies
- config/config.json: Source definitions, pagination, performance
- config/mediator.json: OpenHIM channel and mediator registration
- build-image.sh: Builds Docker image from upstream repo

Pipeline config change:
- fhirServerUrl points to fhir-aggregator_fhir-aggregator:3000/fhir
- No credentials needed (aggregator handles auth to each source)

Mediator source: https://github.com/mherman22/fhir-aggregator-mediator
@mherman22 mherman22 force-pushed the fix/pipeline-shr-integration branch from b39bdc2 to 9fda378 Compare April 1, 2026 11:52
- New nginx config: pipeline.* routes to pipeline controller web UI
- Add reverse-proxy_public network to pipeline docker-compose
- Pipeline control panel accessible via https://pipeline.<domain>/
@mherman22 mherman22 force-pushed the fix/pipeline-shr-integration branch from 9fda378 to 0325026 Compare April 1, 2026 12:05
All 4 instances competing for CPU/memory during simultaneous boot
caused classloader failures and OOM. Now instances start one at a
time, each waiting for HTTP 200 on /metadata before the next starts.

- Scale all to 0 after stack deploy
- Start each instance sequentially with health check (15 min timeout)
- Auto-restart on HTTP 500 (classloader error recovery)
- Instance list configurable via INSTANCES array
- Add deduplication: Patient by identifier (OpenMRS PID), default by resourceId
- Add performance.requestTimeoutMs (120s Express-level timeout)
- Set rejectUnauthorized: false (internal Docker network, no TLS between services)
The previous matching config had only 2 rules with strict exact-match
thresholds (jaro-winkler 1.0), which caused missed matches for:
- Patients with minor name typos ("Jean" vs "Jeen")
- Patients with swapped given/family names ("Jean Baptiste" vs "Baptiste Jean")
- Patients with birthdate entry errors (1985-03-15 vs 1985-03-51)
- Patients missing identifiers (no biometric, no national code)
- Patients with missing birthdates

New matching rules (evaluated in order, first match wins):

1. Biometric exact match — fingerprint (strongest identifier)
2. Code National exact match — national ID alone
3. Code National + full demographics — national ID confirmed by name/gender/DOB
4. iSantePlus ID + full demographics — facility ID confirmed by demographics
5. Exact demographics — name + gender + DOB (no identifier needed)
6. Swapped name detection — given↔family + gender + DOB
7. Fuzzy demographics — jaro-winkler 0.8 for names + DateDamerau 0.8 for DOB
8. Exact names + fuzzy birthdate — catches DOB typos with exact name
9. Code National + partial demographics — for patients with missing DOB
10. Phone + demographics — last resort for patients without identifiers

All rules use Haiti-specific identifier systems:
- http://isanteplus.org/openmrs/fhir2/6-biometrics-national-reference-code
- http://isanteplus.org/openmrs/fhir2/5-code-national
- http://isanteplus.org/openmrs/fhir2/3-isanteplus-id

Rules 5-10 use split thresholds:
- potentialMatchThreshold: 5 (flags for manual review)
- autoMatchThreshold: 6 (auto-links only when ALL fields match)

This prevents false auto-merges while still surfacing potential matches
for human review in the OpenCR dashboard.
mherman22 and others added 7 commits April 4, 2026 13:32
Replaces xds-sender-2.5.0-SNAPSHOT.omod with 2.5.9 built from
IsantePlus/openmrs-module-xds-sender fix/pull-notifications-ssl branch.

Includes fix for SSLPeerUnverifiedException in PullNotificationsTask:
when xdssender.exportCcd.ignoreCerts=true, the OkHttpClient now bypasses
hostname verification — matching the existing behavior for CCD export.

See: IsantePlus/openmrs-module-xds-sender#110

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Built from IsantePlus/openmrs-module-mpi-client fix/proxy-privileges
branch (based on v1.1.4 tag).

Fixes APIAuthenticationException: "Privileges required: Get Identifier
Types" in PatientSyncWorker and PatientUpdateWorker by adding
Context.addProxyPrivilege() for required privileges.

See: IsantePlus/openmrs-module-mpi-client#58

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…fiers

- Add isanteplus, shr-pipeline, openshr to OpenCR clients config so
  the CRUX UI can resolve Submitting System and Point of Service from
  the x-openhim-clientid header
- Add Code PC (9-code-pc) and Biometrics National Reference Code FHIR
  system URI mappings to fhir_patient_identifier_system table so
  iSantePlus sends them with proper system URIs
- Add codepc system entry to OpenCR config for UI label display
- Change exportIdentitiferType key from "Patient ID" to "iSantePlus ID"
  to match the actual OpenMRS identifier type name in the DB
- Add iSante ID mapping to exportIdentitiferType
- Remove ECID from autoXref (generated by CR, not a local identifier)
The SHR mediator queries OpenCR via OpenHIM for golden record
resolution. The CR channel requires authentication. Add
clientRegistryUsername/clientRegistryPassword using the isanteplus
client which has the emr role allowed on the CR channel.
Replace the generic "isanteplus" OpenHIM client with per-facility clients
(hueh, lapaix, ofatma, etc.) so OpenCR can identify which facility
submitted each patient via the x-openhim-clientid header.

- Add FACILITY_ID/FACILITY_NAME env vars to each iSantePlus service
- Update post-start.sh to set xdssender.oshr.username to FACILITY_ID
- Create per-facility OpenHIM clients with emr role
- Add all facilities to OpenCR clients config for CRUX UI display
- Update SHR mediator to use openshr client for CR queries
The decision rules used snake_case espath values (code_national,
isanteplus_id) but Elasticsearch indexes these fields in camelCase
(codeNational). This caused matching rules to never find any
candidates, resulting in separate golden records for patients with
the same Code National across facilities.
@mherman22 mherman22 force-pushed the fix/pipeline-shr-integration branch from d72fe68 to 724a1d7 Compare April 6, 2026 11:52
@mherman22 mherman22 changed the title Fix iSantePlus restart loop, SHR mediator, xds-sender, security, and FHIR data pipeline Fix HIE integration: pipeline, SHR mediator, OpenCR matching, xds-sender, mpi-client, and per-facility identification Apr 6, 2026
Replace the single pipeline (via FHIR aggregator) with one pipeline
per iSantePlus instance, each pointing directly at its FHIR endpoint:

  - pipeline-isanteplus1 → http://isanteplus:8080/openmrs/ws/fhir2/R4
  - pipeline-isanteplus2 → http://isanteplus2:8080/openmrs/ws/fhir2/R4

Both sink to the SHR via OpenHIM. Reduced heap from 2g to 1g per
pipeline since each handles a single instance.

Remove fhir-aggregator-mediator package entirely — not needed when
each site has its own pipeline.
Pipeline-1 runs at :00, Pipeline-2 at :30. Prevents 409 Conflict
errors from duplicate Practitioner/Location UUIDs across cloned
iSantePlus databases when both pipelines write to the SHR simultaneously.
@mherman22 mherman22 changed the title Fix HIE integration: pipeline, SHR mediator, OpenCR matching, xds-sender, mpi-client, and per-facility identification Fix HIE integration: per-instance pipelines, OpenCR matching, module patches, and facility identification Apr 6, 2026
The pipelines are internal services that don't need external access.
Remove the nginx proxy config, reverse-proxy network, and pipeline
subdomain. Add terminal-based pipeline management instructions to
the README including status checks, manual triggers, log inspection,
and how to add pipelines for new instances.
Document the correct docker stack deploy order with dependency notes.
Later packages depend on earlier ones (e.g., pipelines must be last
because they need iSantePlus, OpenHIM, and HAPI FHIR running).
Document both deployment approaches with dependency ordering:
- Instant CLI (recommended): handles env var injection, requires
  build-image.sh after local changes
- Manual docker stack deploy: uses local files directly, useful for
  development and debugging
The mpi-client uses sendingApplication as the Basic auth username and
authtoken as the password when talking to OpenCR via OpenHIM. These
were set to the generic 'isanteplus' which no longer exists as an
OpenHIM client (replaced with per-facility clients).

Update post-start.sh to set both mpi-client.msg.sendingApplication
and mpi-client.security.authtoken to FACILITY_ID.
The per-facility client hashes were generated with sha512(salt + password)
but OpenHIM uses sha512(password + salt). This caused 401 Unauthorized
on all mpi-client and xds-sender requests from iSantePlus to OpenHIM.

Also fix importer password back to instant101 for environments with
persisted MongoDB data.
@mherman22

mherman22 commented Apr 6, 2026

Copy link
Copy Markdown
Author

Hey @ibacher here is the test SHR instance with some data https://shr.sedishtest.live/fhir/Patient, i wanted you to have a look at especially the patient.link for the two patients with the same golden id from opencr. Is this what you envisioned for this? or better improvements could be added and what could those be?

For the instances i am deploying these changes, see https://uwdigi.atlassian.net/wiki/spaces/HAIT/pages/931299355/Roaming+Care+Overview

Also feel free to review this and perhaps merge it so that i can start on a clean PR for the followup commit on your comments here.

@mherman22 mherman22 force-pushed the fix/pipeline-shr-integration branch 2 times, most recently from cd35f11 to 41856d9 Compare April 8, 2026 12:36
mherman22 and others added 4 commits April 8, 2026 17:22
Code PC (patient_identifier_type_id=9) was missing from the
fhir_patient_identifier_system table, causing it to appear in
FHIR/OpenCR without a system URI or label. Map it to
http://isanteplus.org/openmrs/fhir2/9-code-pc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These files are tracked by Git LFS — the prior commit incorrectly
modified the LFS pointer instead of the actual content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Built from mherman22/openmrs-module-mpi-client fix/proxy-privileges
branch rebased on main, which includes:
- Remove getPatient() trigger from MPI sync advice (DIGI-UW#56)
- Skip MPI sync when CR endpoint is not configured (DIGI-UW#57)
- Add proxy privileges to PatientSyncWorker and PatientUpdateWorker (DIGI-UW#58)

Fixes: importing a patient from the MPI no longer creates duplicate
local patients from golden record seealso links.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Includes:
- Implement FhirMpiClientServiceImpl.importPatient() with duplicate check
- Add deduplication guard to PatientUpdateWorker to prevent ghost exports

Fixes: IsantePlus/openmrs-module-mpi-client#59

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ibacher ibacher merged commit 7679c06 into charess-org:main Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants