---
# System prepended metadata

title: HAPI FHIR測試環境建立
tags: [HAPI, FHIR]

---

HAPI FHIR測試環境建立
===

學習FHIR SDK的第一個挑戰往往是如何建立一個合適的測試環境，這份工作，同時包含伺服器架設與測試資料產生。本文說明如何應用Docker快速建立HAPI FHIR Server與使用Synthea產生測試資料。

環境假設
---
系統已經安裝Docker Desktop

Docker Compose
---
由於容器相關技術已經非常成熟，特別是對於測試環境的建立，Docker是最佳選擇。最單純的做法是使用docker-compose.yml，設定成功之後即可重複使用。測試環境將使用HAPI FHIR JPA Server，資料庫則採用Postgres。

基本的docker-compose.yml範例如下：
```
version: '3.7'

services:

  db:
    container_name: db
    image: postgres
    restart: always
    environment:
      POSTGRES_PASSWORD: hapipoc
      POSTGRES_USER: hapipoc
      POSTGRES_DB: hapi
    # not needed for networking between containers but here for troubleshooting
    ports:
      - "5433:5432"

  fhir:
    container_name: fhir
    image: hapiproject/hapi:latest
    ports:
      - "8080:8080"
    environment:
      HAPI_FHIR_USERNAME : admin
      HAPI_FHIR_PASSWORD : admin
      profiles.active: r4
      spring.datasource.url: 'jdbc:postgresql://bmaindb:5432/hapi'
      spring.datasource.username: hapipoc
      spring.datasource.password: hapipoc
      spring.datasource.driverClassName: org.postgresql.Driver
      spring.jpa.properties.hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect
```
由於是測試環境，密碼就直接寫在docker-compose.yml中，正式環境需要採用更安全的作法。YAML檔案內容很直接清楚，就不多做說明，唯一例外是hibernate.dialect，如果不是使用Postgres，相關設定就必須修改。

在"命另提示字元"下與Docker-compose.yml相同的目錄底下執行
```
docker compose up
```
就可以啟動測試環境，包含HAPI FHIR與Postgres


HAPI FHIR特殊設定
---
由於HAPI FHIR有些特殊功能(動態匯入IG、IPS、CR等)並不適用於每一個應用場景，因此會透過環境變數將相關功能enable或disable

- hapi.fhir.ig_runtime_upload_enabled : 可於執行階段動態匯入IG
- hapi.fhir.ips_enabled : 啟用IPS(International Patient Summary)相關功能，主要是$summary operation
- hapi.fhir.cr_enabled : 啟用CR(Clinical Reasoning)相關功能
- hapi.fhir.bulk_export_enabled/hapi.fhir.bulk_import_enabled : 啟用Bulk Data import/export功能

```
hapi.fhir.ig_runtime_upload_enabled: true 
hapi.fhir.ips_enabled: true
hapi.fhir.cr_enabled:true

hapi.fhir.bulk_export_enabled: true
hapi.fhir.bulk_import_enabled: true
```

FHIR另一個特殊應用就是和已經存在的IG結合，提供資料驗證功能。根據IG是否於registry.fhir.org註冊，相關的設定如下：
- 已註冊(以TW Core為例)
```
hapi.fhir.implementationguides.twcore.name: tw.gov.mohw.twcore
hapi.fhir.implementationguides.twcore.version: 0.1.1
hapi.fhir.implementationguides.twcore.reloadExisting: false
hapi.fhir.implementationguides.twcore.installMode: STORE_AND_INSTALL
```
設定匯入TW Core IG很明顯開機時間會拉長，完成後可以從管理畫面觀察到TW Core IG相關的Resource都已匯入。

![test-environment-1](https://hackmd.io/_uploads/HyLVvKev6.png)

- 未註冊(以IPS為例)
```
hapi.fhir.implementationguides.ips_1_1_0.packageUrl: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz
hapi.fhir.implementationguides.ips_1_1_0.name: hl7.fhir.uv.ips
hapi.fhir.implementationguides.ips_1_1_0.version: 1.1.0
```

Synthea - 合成資料產生器
---

建立FHIR Server之後，另一個問題就是測試資料。由於醫療資料具有高度的機敏性，並不適合以實際資料作為練習，所以使用合成資料(Synthetic Data)有其必要性。Synthea為一個開源軟體之醫療資料產生器，官網資料如下：

https://synthetichealth.github.io/synthea/#home

- 下載Synthea
  ```
  git clone https://github.com/synthetichealth/synthea.git
  cd synthea
  ./gradlew build check test
  ```
- 產生合成資料(Synthetic Data)，產生10筆病患資料
  ```
  run_synthea -p 10
  ```

合成資料匯入
---
Synthea產生的資料，預設目錄是/output/fhir。該目錄底下會有一個醫院訊檔案(以hospitalInformation開頭)、一個醫事人員檔案(以practitionerInformation)以及多個Patient檔案(以上述指令為例，雖然預計產生十筆病患資料，但由於模擬過程中，病患可能死亡，因此病患資料可能大於10筆)。

可以用以下HAPI FHIR Java SDK程式碼將資料一起匯入剛建立的HAPI FHIR Server。

```
// Load synthetic data from /output/fhir directory
String path = "../output/fhir";
try {
    Files.walk(Paths.get(path)).forEach(filePath -> {
        if (Files.isRegularFile(filePath)) {
           
            // Only load resources from the /output/fhir directory with file name start with "practitionerinformation"
            if (filePath.toString().contains("practitionerInformation") || filePath.toString().contains("hospitalInformation")) {
                System.out.println("Loading " + filePath);
                // Read the file as a string
                String resourceString = "";
                try {
                    resourceString = new String(Files.readAllBytes(filePath));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                // Parse the string to a FHIR resource
                Bundle bundle = ctx.newJsonParser().parseResource(Bundle.class, resourceString);
                // Use the client to store a new resource instance
                IBaseBundle outcome = client.transaction().withBundle(bundle).execute(); 
            }
        }
        {
            //System.out.println("Skipping " + filePath);
        }
    });
} catch (IOException e) {
    e.printStackTrace();
}
```
由於所有資料都是FHIR Bundle格式，也可以使用REST API POST指令將資料一筆一筆匯入。

Swagger整合
---

HPAI FHIR已經和Swagger整合，輸入`http://localhost:8080/fhir/swagger-ui/#`就可以進入相關畫面：

![test-environment-2](https://hackmd.io/_uploads/r1NfeXWD6.png)

以Parient為例，點選Patient之後，就可以看到以下畫面。若不想查詢Resource的Search Parameter，使用Swagger UI也是一個替代方案。

![test-environment-3](https://hackmd.io/_uploads/rkWBg7ZDa.png)

也可以直接在此頁面測試API

![test-environment-4](https://hackmd.io/_uploads/ry_PbX-D6.png)

延伸閱讀
---
[FHIR REST API介紹](https://hackmd.io/@hongyu0324/fhir-search)
[International Patient Summary (IPS)](https://hackmd.io/@hongyu0324/ips-note)
[HAPI FHIR介紹](https://hackmd.io/@hongyu0324/HapiFhir)

