Skip to content
This repository was archived by the owner on Oct 31, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
99 changes: 99 additions & 0 deletions openfn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# OpenFn Microservice

A demo package using OpenFn Microservice, OpenHIM and HAPI FHIR using Instant OpenHIE.

The `microservice` project.yaml file is located here: [`openfn/docker/config/project.yaml`](./docker/config/project.yaml).

## Getting Started

### Docker

To start up the service

```sh
yarn
yarn docker:build
yarn docker:instant init -t docker openfn
```

To tear down the deployment use the opposing command:

```bash
yarn docker:instant down -t docker openfn
```

To start up the service after a tear down, use the following command:

```bash
yarn docker:instant up -t docker openfn
```

To completely remove all package components use the following option:

```bash
yarn docker:instant destroy -t docker openfn
```

### Kubernetes

To start up the service

```sh
yarn
yarn docker:build
yarn docker:instant init -t k8s openfn
```

To tear down the deployment use the opposing command:

```bash
yarn docker:instant down -t k8s openfn
```

To start up the service after a tear down, use the following command:

```bash
yarn docker:instant up -t k8s openfn
```

To completely remove all package components use the following option:

```bash
yarn docker:instant destroy -t k8s openfn
```

## How data gets to HAPI FHIR

Using the example payload [commcare_sample.json](./fixtures/commcare_sample.json)
we send that to the configured OpenFn Microservice.

Microservice is configurated to run a job based on the shape of the incoming
payload _see [project.yaml](./docker/config/project.yaml)_.

The job `commcare-to-him` will match against this message and will be invoked
performing the following actions:

- creates a payload in the FHIR standard containing
- a Encounter resource that contains (`contained` resource field) a Patient resource
- sends the payload to OpenHIM
- which in turn sends the payload to HAPI FHIR


### In Docker

* URL - `http://localhost:4001`

### In Kubernetes

Use the host ip or DNS, and the port `4001`. If the deployment has been done to a [minikube](https://kubernetes.io/docs/setup/learning-environment/minikube/) cluster, get the external ip and port for the `openfn-service` service by running the following command

```sh
kubectl get services
```

## Notes

- The HAPI FHIR service runs on port `3447`
- The OpenHIM channel that we go through is on port `5001`.
The API is identical, with the exception of a required `Authorization` header.

37 changes: 37 additions & 0 deletions openfn/docker/compose.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash

composeFilePath=$(dirname $(readlink -f $0))

# Create a volume and copy the project configuration in before starting the stack.
docker volume create microservice_him_hapi
docker container create --name dummy -v microservice_him_hapi:/mnt tianon/true
docker cp $composeFilePath/config/project.yaml dummy:/mnt/
docker rm dummy

if [ "$1" == "init" ]; then
docker-compose -p openfn \
-f "$composeFilePath"/docker-compose.yml \
-f "$composeFilePath"/docker-compose.dev.yml \
-f "$composeFilePath"/docker-compose.config.yml \
up -d
elif [ "$1" == "up" ]; then
docker-compose -p openfn \
-f "$composeFilePath"/docker-compose.yml \
-f "$composeFilePath"/docker-compose.dev.yml \
-f "$composeFilePath"/docker-compose.config.yml \
up -d
elif [ "$1" == "down" ]; then
docker-compose -p openfn \
-f "$composeFilePath"/docker-compose.yml \
-f "$composeFilePath"/docker-compose.dev.yml \
-f "$composeFilePath"/docker-compose.config.yml \
stop
elif [ "$1" == "destroy" ]; then
docker-compose -p openfn \
-f "$composeFilePath"/docker-compose.yml \
-f "$composeFilePath"/docker-compose.dev.yml \
-f "$composeFilePath"/docker-compose.config.yml \
down
else
echo "Valid options are: init, up, down, or destroy"
fi
167 changes: 167 additions & 0 deletions openfn/docker/config/project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
jobs:
create-patient:
expression: >
post('Patient', {
body: dataValue('$'),
headers: {
'Content-Type': 'application/json',
Authorization: 'Custom test',
'If-None-Exist': state =>
`identifier=${dataValue('identifier[0].value')(state)}`,
},
});
adaptor: '@openfn/language-http'
trigger: patient-message
credential: openhim

create-organization:
expression: >
post('Organization', {
body: dataValue('$'),
headers: {
'Content-Type': 'application/json',
Authorization: 'Custom test',
'If-None-Exist': state =>
`identifier=${dataValue('identifier[0].value')(state)}`,
},
});
adaptor: '@openfn/language-http'
trigger: organization-message
credential: openhim

commcare-to-openhim:
expression: >
post("Patient", {
body: fields(
field("resourceType", "Patient"),
field("id", dataValue("$form.patient_data.patient_id")),
field("identifier", [
{
use: "usual",
type: {
coding: [
{
system: "http://terminology.hl7.org/CodeSystem/v2-0203",
code: "MR",
},
],
},
system: "urn:oid:0.1.2.3.4.5.6.7",
value: dataValue("$form.patient_data.patient_id"),
},
]),
field("name", [
{
use: "official",
family: dataValue("$form.patient_data.age.last_name"),
given: [dataValue("$form.patient_data.age.first_name")],
},
]),
field("gender", dataValue("$form.patient_data.age.gender")),
field("birthDate", dataValue("$form.patient_data.age.birthDate"))
),
headers: {
"Content-Type": "application/json",
Authorization: "Custom test",
"If-None-Exist": (state) =>
`identifier=${dataValue("$form.patient_data.patient_id")(state)}`,
},
});

post("Encounter", {
body: fields(
field("resourceType", "Encounter"),
field("status", "finished"),
field(
"subject",
field("reference", (state) => `Patient/${dataValue("id")(state)}`)
)
),
headers: {
"Content-Type": "application/json",
Authorization: "Custom test",
},
});
adaptor: '@openfn/language-http'
trigger: commcare-message
credential: openhim

kobo-to-openhim:
expression: >
post("Patient", {
body: fields(
field("resourceType", "Patient"),
field("id", dataValue("$body.Godata_ID")),
field("identifier", [
{
use: "usual",
type: {
coding: [
{
system: "http://terminology.hl7.org/CodeSystem/v2-0203",
code: "MR",
},
],
},
system: "urn:oid:0.1.2.3.4.5.6.7",
value: dataValue("$body.Godata_ID"),
},
]),
field("name", [
{
use: "official",
text: dataValue("$body.Patient_name"),
},
]),
field("gender", dataValue("$body.Sex")),
field("birthDate", (state) => {
const year =
Intl.DateTimeFormat("en", { year: "numeric" }).format() -
Number(dataValue("$body.Age_in_year")(state));
return `${year}-01-01`;
})
),
headers: {
"Content-Type": "application/json",
Authorization: "Custom test",
"If-None-Exist": (state) =>
`identifier=${dataValue("$body.Godata_ID")(state)}`,
},
});

post("Encounter", {
body: fields(
field("resourceType", "Encounter"),
field("status", "finished"),
field(
"subject",
field(
"reference",
(state) => `Patient/${dataValue("id")(state)}`
)
)
),
headers: {
"Content-Type": "application/json",
Authorization: "Custom test",
},
});

adaptor: '@openfn/language-http'
trigger: kobo-message
credential: openhim


triggers:
patient-message:
criteria: '{"resourceType":"Patient"}'
organization-message:
criteria: '{"resourceType":"Organization"}'
kobo-message:
criteria: '{"form":"covid19-registration"}'
commcare-message:
criteria: '{"app_id":"aa"}'

credentials:
openhim:
baseUrl: "http://openhim-core:5001/fhir/"
18 changes: 18 additions & 0 deletions openfn/docker/docker-compose.config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: "3.3"

services:
openfn:
environment:
PROJECT_DIR: /opt/app/project
# MIX_ENV: prod
volumes:
- microservice_him_hapi:/opt/app/project

volumes:
microservice_him_hapi:
external: true

networks:
default:
external:
name: instant_default
6 changes: 6 additions & 0 deletions openfn/docker/docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: '3.3'

services:
openfn:
ports:
- '4001:4001'
10 changes: 10 additions & 0 deletions openfn/docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: '3.3'

services:
openfn:
image: openfn/microservice:v0.3.3
container_name: openfn_him_hapi
environment:
HOST_URL: localhost
PORT: 4001
ENDPOINT_STYLE: async
43 changes: 43 additions & 0 deletions openfn/fixtures/commcare_sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"app_id": "aa",
"archived": false,
"attachments": {
"form.xml": {
"content_type": "text/xml",
"length": 11528,
"url": "https://www.commcarehq.org/a/x.xml"
}
},
"form": {
"#type": "data",
"@name": "Register New Patient",
"@uiVersion": "1",
"@version": "1",
"patient_data": {
"age": {
"age_at_registration_days": "181",
"gender": "female",
"birthDate": "2020-10-11",
"first_name": "test",
"last_name": "case"
},
"locations": {
"city": "k",
"clinic_code": "test_clinic",
"country": "Kenya"
},
"patient_id": "test_clinic-0009-01"
},
"case": {
"@case_id": "xxx",
"@date_modified": "2021-01-21T07:08:19.431000Z",
"@user_id": "aaa",
"@xmlns": "http://commcarehq.org/case/transaction/v2",
"create": {
"case_name": "test case",
"case_type": "patient",
"owner_id": "aaa"
}
}
}
}
Loading