Tag: question

  • Question: How to setup the IBM FHIR Server with fhir-audit?

    The IBM FHIR Server supports audit events for FHIR operations (CREATE-READ-UPDATE-DELETE-OPERATION) in Cloud Auditing Data Federation (CADF) and HL7 FHIR AuditEvent and pushing the events to an Apache Kafka backend. You can read more about it on the IBM FHIR Server site.

    Let’s spin up an IBM FHIR Server with fhir-audit and see what we get with a running container.

    1. Create an Event Streams instance on IBM Cloud Console

    2. Create a topic FHIR_AUDIT

    Create
    1. Click Service Credentials, click Create New Credential, Click Add
    Add a Credential
    1. Expand your new Service Credential, and copy the JSON and store locally, we’ll use it in a subsequent step.
    curl -L https://raw.githubusercontent.com/IBM/FHIR/main/fhir-server/liberty-config/config/default/fhir-server-config-audit-environment.json -o fhir-server-config-audit-environment.json

    The file contains a setting for loading the properties from the Environment, and some basic settings:

            "audit": {
                "serviceClassName" : "com.ibm.fhir.audit.impl.KafkaService",
                "serviceProperties" : {
                    "auditTopic": "FHIR_AUDIT",
                    "geoCity": "Dallas",
                    "geoState": "TX",
                    "geoCounty": "US"
                }
            }
    1. Download the docker image
    docker pull ibmcom/ibm-fhir-server:latest
    
    $ docker pull ibmcom/ibm-fhir-server:latest
    latest: Pulling from ibmcom/ibm-fhir-server
    13897c84ca57: Pull complete 
    64607cc74f9c: Pull complete 
    16ac69f16a48: Pull complete 
    54eaf4e89615: Pull complete 
    215ce34bfb04: Pull complete 
    23fc81994ef3: Pull complete 
    358f256d50db: Pull complete 
    2c5ee9f8d67e: Pull complete 
    df1b462e3122: Pull complete 
    64782415f212: Pull complete 
    5871c358d552: Pull complete 
    931b2daafdbb: Pull complete 
    a77b0c08b2ad: Pull complete 
    8405f4cee973: Pull complete 
    a0d476c441e5: Pull complete 
    d504580ab646: Pull complete 
    b68755501c7d: Pull complete 
    4071f846ff8d: Pull complete 
    Digest: sha256:1829c8ab601355efecc190ef667fba93d92cbbd2e9305c109f736851c3ba97f4
    Status: Downloaded newer image for ibmcom/ibm-fhir-server:latest
    docker.io/ibmcom/ibm-fhir-server:latest
    1. Make the JSON a single line
    cat << EOF | tr -d '\n'
    {
      "api_key": "credentialapikey",
      "apikey": "credentialapikey",
      "iam_apikey_description": "Auto-generated for key 01ba3165-85d9-410b-ad1a-1111",
      "iam_apikey_name": "Service credentials-1",
      "iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",
      "iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::a/11111::serviceid:ServiceId-864b549f-ff90-4b24-84f2-1111",
      "instance_id": "6b00cc8c-26ec-4c42-a1e8-f4da5b9f71e7",
      "kafka_admin_url": "https://admin.eventstreams.cloud.ibm.com",
      "kafka_brokers_sasl": [
        "broker-3.eventstreams.cloud.ibm.com:9093",
        "broker-5.eventstreams.cloud.ibm.com:9093",
        "broker-1.eventstreams.cloud.ibm.com:9093",
        "broker-2.eventstreams.cloud.ibm.com:9093",
        "broker-4.eventstreams.cloud.ibm.com:9093",
        "broker-0.eventstreams.cloud.ibm.com:9093"
      ],
      "kafka_http_url": "https://admin.eventstreams.cloud.ibm.com",
      "password": "credentialapikey",
      "user": "token"
    }
    EOF
    

    The output is:

    {  "api_key": "credentialapikey",  "apikey": "credentialapikey",  "iam_apikey_description": "Auto-generated for key 01ba3165-85d9-410b-ad1a-1111",  "iam_apikey_name": "Service credentials-1",  "iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",  "iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::a/11111::serviceid:ServiceId-864b549f-ff90-4b24-84f2-1111",  "instance_id": "6b00cc8c-26ec-4c42-a1e8-f4da5b9f71e7",  "kafka_admin_url": "https://admin.eventstreams.cloud.ibm.com",  "kafka_brokers_sasl": [    "broker-3.eventstreams.cloud.ibm.com:9093",    "broker-5.eventstreams.cloud.ibm.com:9093",    "broker-1.eventstreams.cloud.ibm.com:9093",    "broker-2.eventstreams.cloud.ibm.com:9093",    "broker-4.eventstreams.cloud.ibm.com:9093",    "broker-0.eventstreams.cloud.ibm.com:9093"  ],  "kafka_http_url": "https://admin.eventstreams.cloud.ibm.com",  "password": "credentialapikey",  "user": "token"}
    
    1. Export ES_CONFIG
    export ES_CONFIG='{  "api_key": "credentialapikey",  "apikey": "credentialapikey",  "iam_apikey_description": "Auto-generated for key 01ba3165-85d9-410b-ad1a-1111",  "iam_apikey_name": "Service credentials-1",  "iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",  "iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::a/11111::serviceid:ServiceId-864b549f-ff90-4b24-84f2-1111",  "instance_id": "6b00cc8c-26ec-4c42-a1e8-f4da5b9f71e7",  "kafka_admin_url": "https://admin.eventstreams.cloud.ibm.com",  "kafka_brokers_sasl": [    "broker-3.eventstreams.cloud.ibm.com:9093",    "broker-5.eventstreams.cloud.ibm.com:9093",    "broker-1.eventstreams.cloud.ibm.com:9093",    "broker-2.eventstreams.cloud.ibm.com:9093",    "broker-4.eventstreams.cloud.ibm.com:9093",    "broker-0.eventstreams.cloud.ibm.com:9093"  ],  "kafka_http_url": "https://admin.eventstreams.cloud.ibm.com",  "password": "credentialapikey",  "user": "token"}'
    

    Note the single quotes surround the above.

    1. Start up the IBM FHIR Server with the EventStreams credentials and fhir-server-config.json we just downloaded.
    docker run --rm -d -p 9443:9443 -e BOOTSTRAP_DB=true \
        -v $(pwd)/fhir-server-config-audit-environment.json:/config/config/default/fhir-server-config.json \
        -e EVENT_STREAMS_AUDIT_BINDING="${ES_CONFIG}" ibmcom/ibm-fhir-server
    

    You’ll see the container id output 60a5f1cae6d677d80772f1736db1be74836a8a4845fcccc81286b7c557bc2d86.

    1. Check that the applications are started using the container id.
    $ docker logs 60a | grep -i started
    [4/21/21, 20:55:58:449 UTC] 00000001 FrameworkMana I   CWWKE0002I: The kernel started after 1.43 seconds
    [4/21/21, 20:55:58:464 UTC] 0000002a FeatureManage I   CWWKF0007I: Feature update started.
    [4/21/21, 20:56:01:328 UTC] 00000030 AppMessageHel A   CWWKZ0001I: Application fhir-openapi started in 1.588 seconds.
    [4/21/21, 20:56:03:141 UTC] 00000031 AppMessageHel A   CWWKZ0001I: Application fhir-bulkdata-webapp started in 3.402 seconds.
    [4/21/21, 20:56:07:824 UTC] 0000002d AppMessageHel A   CWWKZ0001I: Application fhir-server-webapp started in 7.871 seconds.
    [4/21/21, 20:56:07:868 UTC] 0000002a TCPPort       I   CWWKO0219I: TCP Channel defaultHttpEndpoint-ssl has been started and is now listening for requests on host *  (IPv4) port 9443.
    [4/21/21, 20:56:07:880 UTC] 0000002a FeatureManage A   CWWKF0011I: The defaultServer server is ready to run a smarter planet. The defaultServer server started in 10.885 seconds.
    
    1. Download the Sample Data
    curl -L https://raw.githubusercontent.com/IBM/FHIR/main/fhir-server-test/src/test/resources/testdata/everything-operation/Antonia30_Acosta403.json -o Antonia30_Acosta403.json
    
    1. Load the Sample Data bundle to the IBM FHIR Server
    curl -k --location --request POST 'https://localhost:9443/fhir-server/api/v4' \
    --header 'Content-Type: application/fhir+json' \
    --header "Authorization: Basic ${DUMMY_PASSWORD}" \
    --data-binary  "@Antonia30_Acosta403.json" -o response.json
    
    1. Scan the response.json for any status that is not "status": "201". For example, the status is in the family of User Request Error or Server Side Error.

    Note: DUMMY_PASSWORD should be set to your environment.

    1. Download Kafka from Apache Kafka

    2. Create a client-ssl.properties from your service credentials.

    cat << EOF > client-ssl.properties
    bootstrap.servers=broker-2.mybroker.com:9093,broker-5.mybroker.com:9093,broker-0.mybroker.com:9093,broker-3.mybroker.com:9093,broker-4.mybroker.com:9093,broker-1.mybroker.com:9093
    sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="token" password="MYPASSWORD";
    sasl.mechanism=PLAIN
    security.protocol=SASL_SSL
    ssl.protocol=TLSv1.2
    EOF
    
    1. Unzip the kafka archive

    2. Check the Kafka Console Consumer

    bash bin/kafka-console-consumer.sh \
        --bootstrap-server broker-4.mybroker.com:9093,broker-5.mybroker.com:9093,broker-3.mybroker.com:9093,broker-2.mybroker.com:9093,broker-1.mybroker.com:9093,broker-0.mybroker.com:9093 \
        --topic FHIR_AUDIT --max-messages 25 --property print.timestamp=true --consumer.config client-ssl.properties --from-beginning
    

    We see up to 25 messages related to the generated data.

    You see the CADF audit messages, and you can change settings in the fhir-server-config.json to use AuditEvent. You can see an example at https://github.com/IBM/FHIR/blob/main/fhir-server/liberty-config/config/default/fhir-server-config-audit-config.json

  • Question: How to use fhir-path-cli?

    In IBM FHIR Server 4.7.0, the IBM FHIR Server team introduced fhir-path-cli and related to a blog on $everything.

    Now, let’s download some sample data on the server using the Integration Test data.

    1. Download the Sample Data
    curl -L https://raw.githubusercontent.com/IBM/FHIR/main/fhir-server-test/src/test/resources/testdata/everything-operation/Antonia30_Acosta403.json -o Antonia30_Acosta403.json
    
    1. Load the Sample Data bundle to the IBM FHIR Server
    curl -k --location --request POST 'https://localhost:9443/fhir-server/api/v4' \
    --header 'Content-Type: application/fhir+json' \
    --header "Authorization: Basic ${DUMMY_PASSWORD}" \
    --data-binary  "@Antonia30_Acosta403.json" -o response.json
    
    1. Scan the response.json for any status that is not "status": "201". For example, the status is in the family of User Request Error or Server Side Error.

    2. Check the response.json and find the id for Patient (it should be the first one).

            {
                "response": {
                    "id": "178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003",
                    "status": "201",
                    "location": "Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/_history/1",
                    "etag": "W/\"1\"",
                    "lastModified": "2021-04-21T13:34:50.207684Z"
                }
            },
    
    1. Check the Patient/[id]/$everything with a _type constraint and _since.
    • Request *
    curl -k --location --request GET 'https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/$everything?_type=CarePlan,CareTeam&_since=2021-01-01T00:00:00Z&_count=1' \
        --header 'Content-Type: application/fhir+json' \
        --header "Authorization: Basic ${DUMMY_PASSWORD}" -o care.json
    
    • Response *
    {
        "resourceType": "Bundle",
        "id": "689856f9-6ef6-4d74-8f77-a813ff7b1c6d",
        "type": "searchset",
        "total": 11,
        "entry": [
            {
                "fullUrl": "https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003",
                "resource": {
                    "resourceType": "Patient",
                    "id": "178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003",
                    "meta": {
                        "versionId": "1",
                        "lastUpdated": "2021-04-21T13:34:50.207684Z"
                    }
                    ...
                }
            }
        ]
    }
    

    You’ll see further down CareTeam and CarePlan resources…

    1. This is where we can have a bit of fun… download the fhir-path-cli
    curl -L https://github.com/IBM/FHIR/releases/download/4.7.0/fhir-path-4.7.0-cli.jar -o fhir-path-4.7.0-cli.jar
    
    1. Let’s run the fhir-path-4.7.0-cli.jar and test a FHIRPath.

    Command Line

    java -jar fhir-path-4.7.0-cli.jar --path 'entry.fullUrl' --file care.json 
    

    Output

    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CarePlan/178f4a3f86a-1a9a1d73-4d1d-46d5-8494-339dddbfe9d2
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CarePlan/178f4a3f86b-06c07a6c-3be7-4e6a-abb2-040ff67c8c1f
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CarePlan/178f4a3f873-d8069835-938e-45b1-9a70-5aba3794f6f3
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CarePlan/178f4a3f87c-ce204596-8037-416c-b2e0-fc7442d9a276
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CarePlan/178f4a3f87c-46785bc7-807a-40e0-bc08-792977ac8ec8
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CareTeam/178f4a3f86a-b79251c3-5019-426e-8211-748adbdfdf54
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CareTeam/178f4a3f86b-22a7baab-77b3-4fb5-b318-9249ea80ec85
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CareTeam/178f4a3f873-e370fba2-f45e-4cdd-b13b-781e249f84f8
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CareTeam/178f4a3f87c-596d80cc-7b42-4054-bc02-a48acfcc95b7
    https://localhost:9443/fhir-server/api/v4/Patient/178f4a3f869-6abfe1e6-3a4a-4c9a-81a8-fb8c6263e003/CareTeam/178f4a3f87c-6cc197cd-b665-46e0-ad35-c016edeaf13b
    

    You’ve seen a brief introduction to fhir-operation-everything and a bonus fhir-path-cli showing the output.

    Note, DUMMY_PASSWORD should be replaced with or set as a value when the corresponding curl steps are executed.

    Thanks to my colleague that implemented $everything.

  • Question: How to use IBM FHIR Server Notifications with IBM Cloud Functions?

    A question I had recently was “How to use IBM FHIR Server” with IBM Cloud Functions….

    Here is the recipe:

    1 – Create a topic FHIR_NOTIFICATION (for instance in EventStreams)

    2 – Setup a IBM Cloud Function per https://github.com/IBM/ibm-cloud-functions-message-hub-trigger

    3 – Update your process-message with the following code:

    function main(params) {
      return new Promise((resolve, reject) => {
          console.log(params.messages);
        if (!params.messages || !params.messages[0] || !params.messages[0].value) {
          reject("Invalid arguments. Must include 'messages' JSON array with 'value' field");
        }
        const msgs = params.messages;
        const locations = [];
        for (let i = 0; i < msgs.length; i++) {
          const msg = msgs[i];
          console.log(msg.value.location);
          locations.push(msg.value.location);
        }
        resolve({
          locations,
        });
      });
    }
    
    exports.main = main;
    
    

    exports.main = main;

    4 – Invoke with parameters

        {
          "messages": [
            {
              "key": null,
              "offset": 0,
              "partition": 2,
              "topic": "FHIR_NOTIFICATION",
              "value": {
                "datasourceId": "default",
                "lastUpdated": "2021-04-16T13:55:13.312415Z",
                "location": "SearchParameter/178daf6d720-d2da7624-1664-4e50-a4f8-e558ab667fac/_history/1",
                "operationType": "create",
                "resource": {
                  "base": [
                    "Encounter"
                  ],
                  "code": "diff-start-end-time",
                  "description": "Example of sophisiticated extraction",
                  "expression": "iif(period.start.exists() and period.end.exists(), between(period.start , period.end, 'days'), {})",
                  "id": "178daf6d720-d2da7624-1664-4e50-a4f8-e558ab667fac",
                  "meta": {
                    "lastUpdated": "2021-04-16T13:55:13.312415Z",
                    "versionId": "1"
                  },
                  "name": "Example of sophisiticated extraction",
                  "resourceType": "SearchParameter",
                  "status": "draft",
                  "type": "quantity",
                  "url": "https://fhir.ibm.com/demo/diff-start-end-time"
                },
                "resourceId": "178daf6d720-d2da7624-1664-4e50-a4f8-e558ab667fac",
                "tenantId": "default"
              }
            }
          ]
        }
    

    4 – Click Invoke

    5 – Look to the Right to see the logging/activation (Expand the tick on the right-middle of the screen)

    6 – You’ll see you now have a Function connected with EventStreams.

    Notes

    The activation dashboard is important https://cloud.ibm.com/functions/dashboard. You can see great detail on the logs and what is passed to the Function.

    {
      "activationId": "960f129da9f04f138f129da9f0ef13f7",
      "annotations": [
        {
          "key": "path",
          "value": "14d753c7-8620-4caa-b6af-3934e33ae451/fhir-event-streams/process-message"
        },
        {
          "key": "waitTime",
          "value": 274
        },
        {
          "key": "kind",
          "value": "nodejs:12"
        },
        {
          "key": "timeout",
          "value": false
        },
        {
          "key": "limits",
          "value": {
            "concurrency": 1,
            "logs": 10,
            "memory": 256,
            "timeout": 60000
          }
        },
        {
          "key": "initTime",
          "value": 25
        }
      ],
      "duration": 32,
      "end": 1618583048738,
      "logs": [
        "2021-04-16T14:24:08.737951Z    stdout: [",
        "2021-04-16T14:24:08.737997Z    stdout: {",
        "2021-04-16T14:24:08.738031Z    stdout: key: null,",
        "2021-04-16T14:24:08.738037Z    stdout: offset: 0,",
        "2021-04-16T14:24:08.738042Z    stdout: partition: 2,",
        "2021-04-16T14:24:08.738047Z    stdout: topic: 'FHIR_NOTIFICATION',",
        "2021-04-16T14:24:08.738052Z    stdout: value: {",
        "2021-04-16T14:24:08.738057Z    stdout: datasourceId: 'default',",
        "2021-04-16T14:24:08.738062Z    stdout: lastUpdated: '2021-04-16T13:55:13.312415Z',",
        "2021-04-16T14:24:08.738066Z    stdout: location: 'SearchParameter/178daf6d720-d2da7624-1664-4e50-a4f8-e558ab667fac/_history/1',",
        "2021-04-16T14:24:08.738071Z    stdout: operationType: 'create',",
        "2021-04-16T14:24:08.738076Z    stdout: resource: [Object],",
        "2021-04-16T14:24:08.738081Z    stdout: resourceId: '178daf6d720-d2da7624-1664-4e50-a4f8-e558ab667fac',",
        "2021-04-16T14:24:08.738085Z    stdout: tenantId: 'default'",
        "2021-04-16T14:24:08.738134Z    stdout: }",
        "2021-04-16T14:24:08.738141Z    stdout: }",
        "2021-04-16T14:24:08.738146Z    stdout: ]",
        "2021-04-16T14:24:08.738152Z    stdout: SearchParameter/178daf6d720-d2da7624-1664-4e50-a4f8-e558ab667fac/_history/1"
      ],
      "name": "process-message",
      "namespace": "14d753c7-8620-4caa-b6af-3934e33ae451",
      "publish": false,
      "response": {
        "result": {
          "msgs": [
            {
              "key": null,
              "offset": 0,
              "partition": 2,
              "topic": "FHIR_NOTIFICATION",
              "value": {
                "datasourceId": "default",
                "lastUpdated": "2021-04-16T13:55:13.312415Z",
                "location": "SearchParameter/178daf6d720-d2da7624-1664-4e50-a4f8-e558ab667fac/_history/1",
                "operationType": "create",
                "resource": {
                  "base": [
                    "Encounter"
                  ],
                  "code": "diff-start-end-time",
                  "description": "Example of sophisiticated extraction",
                  "expression": "iif(period.start.exists() and period.end.exists(), between(period.start , period.end, 'days'), {})",
                  "id": "178daf6d720-d2da7624-1664-4e50-a4f8-e558ab667fac",
                  "meta": {
                    "lastUpdated": "2021-04-16T13:55:13.312415Z",
                    "versionId": "1"
                  },
                  "name": "Example of sophisiticated extraction",
                  "resourceType": "SearchParameter",
                  "status": "draft",
                  "type": "quantity",
                  "url": "https://fhir.ibm.com/demo/diff-start-end-time"
                },
                "resourceId": "178daf6d720-d2da7624-1664-4e50-a4f8-e558ab667fac",
                "tenantId": "default"
              }
            }
          ]
        },
        "size": 877,
        "status": "success",
        "success": true
      },
      "start": 1618583048706,
      "subject": "pb",
      "version": "0.0.1"
    }
    
  • Question: How do I decode the Job Id from the Bulk Data Export or Import on the IBM FHIR Server?

    Question: How do I decode the Job Id from the Bulk Data Export or Import on the IBM FHIR Server?

    You can use the following code to walk through and decode your job id using your passowrd.

  • Question: How do I configure fhir-notification Kafka so I don’t have to use a keystore?

    The IBM FHIR Server provides an eventing service that notifies about persistence events – CUD (Create-Update-Delete). The notification service can trigger specific actions in a downstream application. You can configure these events to flow to Apache Kafka.

    If you want to configure the IBM FHIR Server without a keystore, you can configure SASL_SSL like the below (notice there is no keystore specified).

    {
        "fhirServer":{
            "notifications":{
                "kafka": {
                    "enabled": true,
                    "topicName": "FHIR_NOTIFICATIONS",
                    "connectionProperties": {
                        "bootstrap.servers": "broker-1.mybroker.com:9093,broker-2.mybroker.com:9093,broker-0.mybroker.com:9093,broker-5.mybroker.com:9093,broker-4.mybroker.com:9093,broker-3.mybroker.com:9093",
                        "security.protocol": "SASL_SSL",
                        "sasl.mechanism": "PLAIN",
                        "ssl.protocol": "TLSv1.2",
                        "ssl.enabled.protocols": "TLSv1.2",
                        "ssl.endpoint.identification.algorithm": "HTTPS",
                        "security.inter.broker.protocol": "SSL",
                        "sasl.jaas.config": "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"token\" password=\"MYPASSWORD\";",
                        "acks": "all",
                        "retries": "60",
                        "request.timeout.ms": "10000",
                        "max.block.ms": "60000",
                        "max.in.flight.requests.per.connection": "5",
                        "client.dns.lookup": "use_all_dns_ips"
                    }
                }
            }
        }
    }
    

    If you run a workload against IBM FHIR Server, it’ll generate a set of notifications…

    1. Download Kafka from https://kafka.apache.org

    2. Create a client-ssl.properties

    cat << EOF > client-ssl.properties
    bootstrap.servers=broker-2.mybroker.com:9093,broker-5.mybroker.com:9093,broker-0.mybroker.com:9093,broker-3.mybroker.com:9093,broker-4.mybroker.com:9093,broker-1.mybroker.com:9093
    sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="token" password="MYPASSWORD";
    sasl.mechanism=PLAIN
    security.protocol=SASL_SSL
    ssl.protocol=TLSv1.2
    EOF
    
    1. Unzip the kafka archive

    2. Check the Kafka Console Consumer

    bash bin/kafka-console-consumer.sh --bootstrap-server broker-4.mybroker.com:9093,broker-5.mybroker.com:9093,broker-3.mybroker.com:9093,broker-2.mybroker.com:9093,broker-1.mybroker.com:9093,broker-0.mybroker.com:9093 --topic FHIR_NOTIFICATION --max-messages 25 --property print.timestamp=true --consumer.config client-ssl.properties
    
  • Question: Can I validate a batch of FHIR resources?

    Yes, you can use the Batch API.

    1. The Bundle.entry must include a request object pointing at URL <RESOURCE>/$validate with method POST.

    2. The Resource that is validated must be wrapped in a Parameters object and added to the Parameters.parameter.resource

    {
        "resourceType": "Parameters",
        "parameter": [
            {
                "name": "resource",
                "resource": <COPY HERE>
            }
        ]
    }
    
    1. Embed it at Bundle.entry.resource, and make a request to the server root.
    curl --location --request POST 'https://localhost:9443/fhir-server/api/v4' \
    --header 'Authorization: Basic ...' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "resourceType": "Bundle",
        "type": "transaction",
        "entry": [
            {
                "fullUrl": "Patient/$validate",
                "resource": {
                    "resourceType": "Parameters",
                    "parameter": [
                        {
                            "name": "resource",
                            "resource": {
                                "resourceType": "Patient",
                                "id": "example",
                                "name": [
                                    {
                                        "use": "official",
                                        "family": "Chalmers",
                                        "given": [
                                            "Peter",
                                            "James"
                                        ]
                                    }
                                ]
                            }
                        }
                    ],
                    "meta": {
                        "tag": [
                            {
                                "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason",
                                "code": "HTEST",
                                "display": "test health data"
                            }
                        ]
                    }
                },
                "request": {
                    "method": "POST",
                    "url": "Patient/$validate"
                }
            }
        ]
    }'
    
    1. You’ll get a response like this, note the HTTP Status Code is 200 coming back… You have to look at Bundle.entry.resource.response.issue to check the severity and details to see why the validation is failing.
    {
        "resourceType": "Bundle",
        "type": "transaction-response",
        "entry": [
            {
                "resource": {
                    "resourceType": "OperationOutcome",
                    "id": "NoError",
                    "text": {
                        "status": "additional",
                        "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p>No error</p></div>"
                    },
                    "issue": [
                        {
                            "severity": "warning",
                            "code": "invariant",
                            "details": {
                                "text": "dom-6: A resource should have narrative for robust management"
                            },
                            "expression": [
                                "Patient"
                            ]
                        }
                    ]
                },
                "response": {
                    "status": "200",
                    "outcome": {
                        "resourceType": "OperationOutcome",
                        "id": "NoError",
                        "text": {
                            "status": "additional",
                            "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p>No error</p></div>"
                        },
                        "issue": [
                            {
                                "severity": "warning",
                                "code": "invariant",
                                "details": {
                                    "text": "dom-6: A resource should have narrative for robust management"
                                },
                                "expression": [
                                    "Patient"
                                ]
                            }
                        ]
                    }
                }
            }
        ]
    }
    

    You should add entries for each Resource you want to validate in the Batch.