diff --git a/config.yaml b/config.yaml index e883f4a5..e561e5a9 100644 --- a/config.yaml +++ b/config.yaml @@ -24,6 +24,7 @@ packages: - identity-access-manager-keycloak - openhim-mapping-mediator - database-postgres + - fhir-ig-importer profiles: - name: cdr-dw diff --git a/fhir-ig-importer/docker-compose.dev.yml b/fhir-ig-importer/docker-compose.dev.yml new file mode 100644 index 00000000..25714fbd --- /dev/null +++ b/fhir-ig-importer/docker-compose.dev.yml @@ -0,0 +1,14 @@ +version: '3.9' + +services: + fhir-ig-importer-mediator: + ports: + - target: 3001 + published: 3333 + mode: host + + fhir-ig-importer-ui: + ports: + - target: 8080 + published: 3000 + mode: host diff --git a/fhir-ig-importer/docker-compose.yml b/fhir-ig-importer/docker-compose.yml new file mode 100644 index 00000000..43c0be1a --- /dev/null +++ b/fhir-ig-importer/docker-compose.yml @@ -0,0 +1,30 @@ +version: '3.9' + +services: + fhir-ig-importer-mediator: + image: jembi/fhir-ig-importer-mediator:${FHIR_IG_IMPORTER_CORE_VERSION} + networks: + hapi-fhir: + openhim: + environment: + HAPI_FHIR_BASE_URL: ${HAPI_FHIR_BASE_URL} + HAPI_FHIR_INSTANCES: ${HAPI_FHIR_INSTANCES} + FHIR_IG_IMPORTER_CORE_PORT: ${FHIR_IG_IMPORTER_CORE_PORT} + FHIR_IG_IMPORTER_CORE_HOST: ${FHIR_IG_IMPORTER_CORE_HOST} + + fhir-ig-importer-ui: + image: jembi/fhir-ig-importer-mediator-ui:${FHIR_IG_IMPORTER_UI_VERSION} + networks: + hapi-fhir: + openhim: + environment: + FHIR_IG_IMPORTER_CORE_URL: ${FHIR_IG_IMPORTER_CORE_URL} + +networks: + hapi-fhir: + name: hapi-fhir_public + external: true + openhim: + name: openhim_public + external: true + diff --git a/fhir-ig-importer/importer/docker-compose.config.yml b/fhir-ig-importer/importer/docker-compose.config.yml new file mode 100644 index 00000000..2f47ef17 --- /dev/null +++ b/fhir-ig-importer/importer/docker-compose.config.yml @@ -0,0 +1,41 @@ +version: '3.9' + +services: + # container for executing config import scripts for creating the OpenHIM channels used by the Mediator + fhir-ig-importer-config-importer: + image: node:erbium-alpine + networks: + openhim: + default: + environment: + OPENHIM_API_USERNAME: ${OPENHIM_USERNAME} + OPENHIM_API_PASSWORD: ${OPENHIM_PASSWORD} + # Reject unauthorised is only needed if the OpenHIM's SSL is not setup + NODE_TLS_REJECT_UNAUTHORIZED: 0 + command: sh -c "node openhimConfig.js" + configs: + - source: fhir-ig-importer-config-importer-openhimConfig.js + target: /openhimConfig.js + - source: fhir-ig-importer-config-importer-openhim-import.json + target: /openhim-import.json + deploy: + replicas: 1 + restart_policy: + condition: none + +configs: + fhir-ig-importer-config-importer-openhimConfig.js: + file: ./volume/openhimConfig.js + name: fhir-ig-importer-config-importer-openhimConfig.js-${fhir_ig_importer_config_importer_openhimConfig_js_DIGEST:?err} + labels: + name: fhir-ig-importer + fhir-ig-importer-config-importer-openhim-import.json: + file: ./volume/openhim-import.json + name: fhir-ig-importer-config-importer-openhim-import.json-${fhir_ig_importer_config_importer_openhim_import_js_DIGEST:?err} + labels: + name: fhir-ig-importer + +networks: + openhim: + name: openhim_public + external: true diff --git a/fhir-ig-importer/importer/volume/openhim-import.json b/fhir-ig-importer/importer/volume/openhim-import.json new file mode 100644 index 00000000..ea763a9b --- /dev/null +++ b/fhir-ig-importer/importer/volume/openhim-import.json @@ -0,0 +1,326 @@ +{ + "Users": [ + { + "firstname": "Super", + "surname": "User", + "email": "root@openhim.org", + "provider": "token", + "groups": ["admin"], + "passwordAlgorithm": null, + "passwordHash": null, + "passwordSalt": null, + "expiry": null, + "locked": false, + "token": null, + "tokenType": null + } + ], + "Clients": [ + { + "clientID": "instant-client", + "name": "Instant Client", + "roles": ["instant"], + "customTokenID": "test" + } + ], + "Channels": [ + { + "name": "IG Importer Get", + "urlPattern": "^/ig.*$", + "methods": ["GET", "OPTIONS"], + "type": "http", + "tcpPort": null, + "tcpHost": null, + "pollingSchedule": null, + "requestBody": true, + "responseBody": true, + "allow": [], + "whitelist": [], + "authType": "public", + "routes": [ + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "get fhir igs", + "secured": false, + "host": "fhir-ig-importer-mediator", + "port": 3001, + "path": "/ig/v1.0/all", + "pathTransform": "", + "primary": true, + "username": "", + "password": "" + } + ], + "matchContentTypes": [], + "matchContentRegex": null, + "matchContentXpath": null, + "matchContentJson": null, + "matchContentValue": null, + "properties": [], + "txViewAcl": [], + "txViewFullAcl": [], + "txRerunAcl": [], + "status": "enabled", + "rewriteUrls": false, + "addAutoRewriteRules": true, + "rewriteUrlsConfig": [], + "autoRetryEnabled": false, + "autoRetryPeriodMinutes": 60, + "updatedBy": { + "id": "6581971ea4e841f92ec4321a", + "name": "Super User" + }, + "alerts": [] + }, + { + "methods": ["POST", "OPTIONS"], + "type": "http", + "allow": [], + "whitelist": [], + "authType": "public", + "matchContentTypes": [], + "properties": [], + "txViewAcl": [], + "txViewFullAcl": [], + "txRerunAcl": [], + "status": "enabled", + "rewriteUrls": false, + "addAutoRewriteRules": true, + "autoRetryEnabled": false, + "autoRetryPeriodMinutes": 60, + "updatedBy": { + "id": "6581971ea4e841f92ec4321a", + "name": "Super User" + }, + "urlPattern": "^/ig.*$", + "tcpPort": null, + "tcpHost": null, + "pollingSchedule": null, + "requestBody": true, + "responseBody": true, + "routes": [ + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "upload fhir ig", + "secured": false, + "host": "fhir-ig-importer-mediator", + "port": 3001, + "path": "/ig/v1.0/upload", + "pathTransform": "", + "primary": true, + "username": "", + "password": "" + } + ], + "matchContentRegex": null, + "matchContentXpath": null, + "matchContentJson": null, + "matchContentValue": null, + "rewriteUrlsConfig": [], + "alerts": [], + "name": "IG Importer Upload" + }, + { + "methods": ["DELETE", "OPTIONS"], + "type": "http", + "allow": [], + "whitelist": [], + "authType": "public", + "matchContentTypes": [], + "properties": [], + "txViewAcl": [], + "txViewFullAcl": [], + "txRerunAcl": [], + "status": "enabled", + "rewriteUrls": false, + "addAutoRewriteRules": true, + "autoRetryEnabled": false, + "autoRetryPeriodMinutes": 60, + "updatedBy": { + "id": "6581971ea4e841f92ec4321a", + "name": "Super User" + }, + "urlPattern": "^/ig/v1.0/delete.*$", + "tcpPort": null, + "tcpHost": null, + "pollingSchedule": null, + "requestBody": true, + "responseBody": true, + "routes": [ + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "delete fhir ig", + "secured": false, + "host": "fhir-ig-importer-mediator", + "port": 3001, + "path": "", + "pathTransform": "", + "primary": true, + "username": "", + "password": "" + } + ], + "matchContentRegex": null, + "matchContentXpath": null, + "matchContentJson": null, + "matchContentValue": null, + "rewriteUrlsConfig": [], + "alerts": [], + "name": "IG Importer Delete" + }, + { + "methods": ["PUT", "OPTIONS"], + "type": "http", + "allow": [], + "whitelist": [], + "authType": "public", + "matchContentTypes": [], + "properties": [], + "txViewAcl": [], + "txViewFullAcl": [], + "txRerunAcl": [], + "status": "enabled", + "rewriteUrls": false, + "addAutoRewriteRules": true, + "autoRetryEnabled": false, + "autoRetryPeriodMinutes": 60, + "updatedBy": { + "id": "6581971ea4e841f92ec4321a", + "name": "Super User" + }, + "urlPattern": "^/ig/v1.0/update.*$", + "tcpPort": null, + "tcpHost": null, + "pollingSchedule": null, + "requestBody": true, + "responseBody": true, + "routes": [ + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "update fhir ig", + "secured": false, + "host": "fhir-ig-importer-mediator", + "port": 3001, + "path": "", + "pathTransform": "", + "primary": true, + "username": "", + "password": "" + } + ], + "matchContentRegex": null, + "matchContentXpath": null, + "matchContentJson": null, + "matchContentValue": null, + "rewriteUrlsConfig": [], + "alerts": [], + "name": "IG Importer Update" + } + ], + "Mediators": [ + { + "urn": "urn:mediator:fhir-ig-importer-mediator", + "version": "1.0.0", + "name": "Fhir IG importer mediator", + "description": "Mediator that upload, delete fhir IG in Fhir server.", + "defaultChannelConfig": [ + { + "methods": ["POST"], + "type": "http", + "allow": ["instant"], + "whitelist": [], + "authType": "private", + "matchContentTypes": [], + "properties": [], + "txViewAcl": [], + "txViewFullAcl": [], + "txRerunAcl": [], + "status": "enabled", + "rewriteUrls": false, + "addAutoRewriteRules": true, + "autoRetryEnabled": false, + "autoRetryPeriodMinutes": 60, + "name": "Fhir IG importer mediator", + "urlPattern": "^/ig", + "routes": [ + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "mediator main entry", + "host": "fhir-ig-importer-mediator", + "port": 3001, + "primary": true + } + ], + "alerts": [], + "rewriteUrlsConfig": [] + } + ], + "endpoints": [ + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "upload fhir ig", + "host": "fhir-ig-importer-mediator", + "port": 3001, + "path": "/ig/v1.0/upload", + "primary": true + }, + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "get fhir igs", + "host": "fhir-ig-importer-mediator", + "port": 3001, + "path": "/ig/v1.0/all", + "primary": true + }, + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "delete fhir ig", + "host": "fhir-ig-importer-mediator", + "port": 3001, + "path": "/ig/v1.0/delete/:id", + "primary": true + }, + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "get fhir ig", + "host": "fhir-ig-importer-mediator", + "port": 3001, + "path": "/ig/v1.0/info/:id", + "primary": true + }, + { + "type": "http", + "status": "enabled", + "forwardAuthHeader": false, + "name": "update fhir ig", + "host": "fhir-ig-importer-mediator", + "port": 3001, + "path": "/ig/v1.0/update/:id", + "primary": true + } + ], + "configDefs": [], + "_lastHeartbeat": "2023-12-22T10:52:05.461Z", + "_uptime": 1191.2140364 + } + ], + "ContactGroups": [] +} diff --git a/fhir-ig-importer/importer/volume/openhimConfig.js b/fhir-ig-importer/importer/volume/openhimConfig.js new file mode 100644 index 00000000..182ee887 --- /dev/null +++ b/fhir-ig-importer/importer/volume/openhimConfig.js @@ -0,0 +1,53 @@ +"use strict"; + +const fs = require("fs"); +const https = require("https"); +const path = require("path"); + +const OPENHIM_CORE_SERVICE_NAME = "openhim-core"; +const OPENHIM_MEDIATOR_API_PORT = 8080; +const OPENHIM_API_PASSWORD = process.env.OPENHIM_API_PASSWORD || "instant101"; +const OPENHIM_API_USERNAME = + process.env.OPENHIM_API_USERNAME || "root@openhim.org"; + +const authHeader = new Buffer.from( + `${OPENHIM_API_USERNAME}:${OPENHIM_API_PASSWORD}` +).toString("base64"); + +const jsonData = JSON.parse( + fs.readFileSync(path.resolve(__dirname, "openhim-import.json")) +); + +const data = JSON.stringify(jsonData); + +const options = { + protocol: "https:", + hostname: OPENHIM_CORE_SERVICE_NAME, + port: OPENHIM_MEDIATOR_API_PORT, + path: "/metadata", + method: "POST", + headers: { + "Content-Type": "application/json", + "Content-Length": data.length, + Authorization: `Basic ${authHeader}`, + }, +}; + +const req = https.request(options, (res) => { + if (res.statusCode == 401) { + throw new Error(`Incorrect OpenHIM API credentials`); + } + + if (res.statusCode != 201) { + throw new Error(`Failed to import OpenHIM config: ${res.statusCode}`); + } + + console.log("Successfully Imported OpenHIM Config"); +}); + +req.on("error", (error) => { + throw new Error(`Failed to import OpenHIM config: ${error}`); +}); + +req.write(data); +req.end(); diff --git a/fhir-ig-importer/package-metadata.json b/fhir-ig-importer/package-metadata.json new file mode 100644 index 00000000..1db85f0a --- /dev/null +++ b/fhir-ig-importer/package-metadata.json @@ -0,0 +1,20 @@ +{ + "id": "fhir-ig-importer", + "name": "FHIR IG Importer", + "description": "The package provides mechanism of loading a fhir IG and deploying to the fhir server. The package has a microfrontend and mediator app", + "type": "infrastructure", + "version": "0.0.1", + "dependencies": [ + "interoperability-layer-openhim", + "fhir-datastore-hapi-fhir" + ], + "environmentVariables": { + "HAPI_FHIR_BASE_URL": "http://hapi-fhir:8080/fhir", + "HAPI_FHIR_INSTANCES": 1, + "FHIR_IG_IMPORTER_CORE_PORT": 3001, + "FHIR_IG_IMPORTER_CORE_HOST": "0.0.0.0", + "FHIR_IG_IMPORTER_CORE_URL": "http://0.0.0.0:3001/fhir/ig/v1.0", + "FHIR_IG_IMPORTER_UI_VERSION": "0.1.0", + "FHIR_IG_IMPORTER_CORE_VERSION": "1.0.0" + } +} diff --git a/fhir-ig-importer/swarm.sh b/fhir-ig-importer/swarm.sh new file mode 100644 index 00000000..900b1325 --- /dev/null +++ b/fhir-ig-importer/swarm.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +declare ACTION="" +declare MODE="" +declare COMPOSE_FILE_PATH="" +declare UTILS_PATH="" +declare STACK="fhir-ig-importer" + +function init_vars() { + ACTION=$1 + MODE=$2 + + COMPOSE_FILE_PATH=$( + cd "$(dirname "${BASH_SOURCE[0]}")" || exit + pwd -P + ) + UTILS_PATH="${COMPOSE_FILE_PATH}/../utils" + readonly ACTION + readonly MODE + readonly COMPOSE_FILE_PATH + readonly UTILS_PATH + readonly STACK +} +# shellcheck disable=SC1091 +function import_sources() { + source "${UTILS_PATH}/docker-utils.sh" + source "${UTILS_PATH}/config-utils.sh" + source "${UTILS_PATH}/log.sh" +} + +function initialize_package() { + local fhir_ig_importer_dev_compose_filename="" + + if [[ "${MODE}" == "dev" ]]; then + log info "Running package in DEV mode" + fhir_ig_importer_dev_compose_filename="docker-compose.dev.yml" + else + log info "Running package in PROD mode" + fi + + ( + docker::deploy_service "$STACK" "${COMPOSE_FILE_PATH}" "docker-compose.yml" "$fhir_ig_importer_dev_compose_filename" + ) || { + log error "Failed to deploy package" + exit 1 + } + docker::deploy_config_importer $STACK "$COMPOSE_FILE_PATH/importer/docker-compose.config.yml" "fhir-ig-importer-config-importer" "fhir-ig-importer" + +} + +function destroy_package() { + docker::stack_destroy "$STACK" +} + +main() { + init_vars "$@" + import_sources + + if [[ "${ACTION}" == "init" ]] || [[ "${ACTION}" == "up" ]]; then + if [[ "${CLUSTERED_MODE}" == "true" ]]; then + log info "Running package in Cluster node mode" + else + log info "Running package in Single node mode" + fi + + initialize_package + elif [[ "${ACTION}" == "down" ]]; then + log info "Scaling down package" + + docker::scale_services "$STACK" 0 + elif [[ "${ACTION}" == "destroy" ]]; then + log info "Destroying package" + destroy_package + else + log error "Valid options are: init, up, down, or destroy" + fi +} + +main "$@" diff --git a/interoperability-layer-openhim/docker-compose.yml b/interoperability-layer-openhim/docker-compose.yml index 8fd88c41..0728edc2 100644 --- a/interoperability-layer-openhim/docker-compose.yml +++ b/interoperability-layer-openhim/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.9" services: openhim-core: - image: jembi/openhim-core:v8.4.0 + image: ${OPENHIM_CORE_IMAGE} networks: kafka: hapi-fhir: diff --git a/interoperability-layer-openhim/package-metadata.json b/interoperability-layer-openhim/package-metadata.json index 0d01c755..9bfef8e2 100644 --- a/interoperability-layer-openhim/package-metadata.json +++ b/interoperability-layer-openhim/package-metadata.json @@ -6,6 +6,7 @@ "type": "infrastructure", "dependencies": [], "environmentVariables": { + "OPENHIM_CORE_IMAGE": "jembi/openhim-core:microfrontends-pre-release", "MONGO_SET_COUNT": "1", "OPENHIM_CORE_INSTANCES": "1", "OPENHIM_CONSOLE_INSTANCES": "1", @@ -26,7 +27,7 @@ "OPENHIM_MONGO_MEMORY_RESERVE": "500M", "OPENHIM_MONGO_URL": "mongodb://mongo-1:27017/openhim", "OPENHIM_MONGO_ATNAURL": "mongodb://mongo-1:27017/openhim", - "OPENHIM_CONSOLE_VERSION": "jembi/openhim-console:v1.18.2", + "OPENHIM_CONSOLE_VERSION": "jembi/openhim-console:microfrontend-poc", "KAFKA_HOSTS": "kafka-01:9092", "KC_REALM_NAME": "platform-realm", "KC_FRONTEND_URL": "http://localhost:9088", @@ -34,6 +35,7 @@ "KC_OPENHIM_CLIENT_ID": "openhim-oauth", "KC_OPENHIM_CLIENT_SECRET": "tZKfEbWf0Ka5HBNZwFrdSyQH2xT1sNMR", "KC_OPENHIM_ROOT_URL": "http://localhost:9000", - "KC_API_URL": "http://identity-access-manager-keycloak:8080" + "KC_API_URL": "http://identity-access-manager-keycloak:8080", + "OPENHIM_CONSOLE_BASE_URL": "http://localhost:9000" } }