# Implementation of CDA Exchange Plus Import

This document details some basic configuration settings to use when setting up CDA Exchange Plus.

Before implementing, it is strongly recommended the [planning document for CDA](/content/docs/guide_planning/planning_cda_import.html) is reviewed to ensure the necessary considerations have been made for a smooth implementation and testing/validating of the CDA Exchange Plus.

## Checklist

- [ ]  Install SmileCDR

- [ ]  add licence to enable CDA Exchange Plus
- [ ]  configure CDA Exchange Plus Module with the licence - if already configured, skip this section

- [ ]  if using Helm charts, go to: method 1, skip other methods
  - [ ]  if using Docker or Properties mode, go to: method 2, skip other methods.
  - [ ]  if using WebAdmin Console, go to: method 3, skip other methods
- [ ]  configure Message Broker - if already configured, skip this section

- [ ]  if using Channel Import, go to: Channel Import
  - [ ]  if using Camel, go to: Camel

- [ ]  configure camel routes
- [ ]  Troubleshooting CDA Exchange Plus

## Installing SmileCDR and configuring the CDA Exchange Plus

1. **Install SmileCDR**

SmileCDR can be installed via a couple of ways, via [tarball](/content/docs/quickstart/smile_quickstart.html), [using Docker](/content/docs/guide_installation/docker_container_installation.html), or [using helm chart](https://smilecdr-public.gitlab.io/smile-dh-helm-charts/latest/guide/).

It is recommended the latest version of smileCDR is used with CDA Exchange Plus, at minimum 2025.02.R01 or later.

2. **Add License**

CDA Exchange Plus is a premium module, as such a license is required in order for it to work. The licence can be added via helm chart, properties file or via the webadmin console.

**Via Helm chart** The SmileCDR licence is a regular JWT token and can be stored in an AWS Secrets Manager Secret under the `jwt` key in the JSON object. It can then be added to the values file via the following as defined by the [secretSpec schema](https://smilecdr-public.gitlab.io/smile-dh-helm-charts/latest/guide/secrets/configuring-secrets/):

```yaml
license:
 type: sscsi
 provider: aws
 secretArn: arn:aws:secretsmanager:us-east-1:111111111111:secret:my-smile-license
```

Copy

**Properties file** The properties file can take either a location to the file containing the JWT token (JWT\_file), or the JWT token directly (JWT\_token).

```properties
module.license.type=LICENSE

module.license.config.jwt_file=
module.license.config.jwt_text={JWT_token}
```

Copy

**Webadmin console** In the webadmin console, navigate to: Configuration >> Module Config >> license.

Under this module there are two settings: **License JWT File** will allow you to point to the file containing the JWT token, while **License JWT Text** will allow you to paste the JWT token directly into the module.

Once a selection has been made, save the config and restart the module. If all has gone correctly, the UI should update and list which modules the license allows access to.

3. **Configuring the CDA Exchange Plus module**

The CDA Exchange Plus module can be configured in a few different ways:

**Method 1:** Via helm chart SmileCDR’s helm chart implementation allows for a simplified installation and configuration of SmileCDR on Kubernetes using a number of best practices. This allows you to not just configure SmileCDR but the environment around it as well.

The following code must be added to the `values.yaml` file in order for the helm chart to properly configure the CDA Exchange Plus module, the properties must fall under the `modules:` header:

```yaml
{module_id}:
 name: CDA EXCHANGE PLUS
 enabled: true
 type: CDA_EXCHANGE_PLUS
 requires:
      PERSISTENCE_ALL: {module_id_of_persistence_module}
    config:
      target_ig: {IG_type}
      ecmascript_module: false
      prefer_supplied_translations: false
      store_original_ccd: true
      cda_processing_script.file: {processing_script_file_location}
      interceptor_bean_types: {interceptor_bean}
```

Copy

Where:

- `{module_id}`is the name you gave the CDA Exchange Module

- `{module_id_of_persistence_module}` is the name of your persistence module
  - `{IG_type}` is the IG the CDA Module will map against, there are currently four IGs available: `US_CORE_IG`, `FHIR_CORE_PROFILE`, `USCDI_V2` and `USCDI_V3`.
  - OPTIONAL: `{processing_script_file_location}` is the location of the JavaScript processing script for CDA
  - OPTIONAL: `{interceptor_bean}` is the name of the interceptor class for the CDA Exchange Plus Java Interceptor

Once all has been configured the helmchart can be spun up as usual.

**Method 2:** Via the Properties file If you are installing SmileCDR via Docker or tarball using \[Properties Mode\](/docs/guide\_installation/configuring\_smile\_cdr.html#module-property-source). The following properties in the `cdr-Master-config.properties` file must be set:

- `Module.license.config.jwt_text` \- should contain the SmileCDR licence for CDA Exchange Plus (and any subsequent other licenced modules you may be using)

module.cda\_exchange\_plus.type=CDA\_EXCHANGE\_PLUS
- The following CDA Exchange Plus module settings must also be set where:
  - `{module_id}` is the name you gave the CDA Exchange Module
  - `{module_id_of_persistence_module}` is the name of your persistence module
  - `{IG_type}` is the IG the CDA Module will map against, there are currently four IGs available: `US_CORE_IG`, `FHIR_CORE_PROFILE`, `USCDI_V2` and `USCDI_V3`.
  - OPTIONAL: `{processing_script_file_location}` is the location of the JavaScript processing script for CDA
  - OPTIONAL: `{interceptor_bean}` is the name of the interceptor class for the CDA Exchange Plus Java Interceptor

```properties
module.{module_id}.type=CDA_EXCHANGE_PLUS
module.{module_id}.config.store_original_ccd=true
module.{module_id}.requires.PERSISTENCE_ALL={module_id_of_persistence_module}
module.{module_id}.config.target_ig={IG_type}
module.{module_id}.config.cda_processing_script.file={processing_script_file_location}
module.{module_id}.config.interceptor_bean_types={interceptor_bean}\
```

Copy

Once the properties have been configured if configured via tarball run the following command to spin up SmileCDR: `bin/smilecdr start`

If spinning up via Docker, run the docker command to start up the pods.

**Method 3:** Via the webadmin console This method requires SmileCDR to already be running in the environment.

1. Run SmileCDR by running the following command:

`bin/smilecdr start`
2. Navigate to the Web Admin Console via `http://localhost:9100` or the domain URL used for it.
3. Install the license: Configuration >> Module Config >> License, paste the licence key into Licence JWT Text, click Save Config and restart the License module.
4. Navigate back to the main WebAdmin Console page via Configuration >> Module Config >> Add Module
5. Select CDA Exchange+ from the dropdown menu and select Add.
6. In Dependencies, under FHIR Storage Module (any FHIR version) select the persistence module used for your database.
7. Click Create Module.
8. Optionally, script and interceptor can be added via the webadmin console as well under the appropriate headers.

## Adding Message brokers

CDA Exchange Plus by default uses REST API to ingest CCDs via the `$sdh.import-cda` operation. However, it is possible to have CDA documents ingested via a Message broker.

SmileCDR currently has two modules that can be used for message brokers, [Channel Import](/content/docs/channel_import/channel_import_overview.html#channel-import-overview/index.html) and [Camel](/content/docs/camel/camel_module_overview.html).

**Pre-requisites**: Kafka must be set up prior to configuring Channel Import or Camel.

**Configuring Channel Import**
Using the same methods as mentioned above, Channel Import can be added via the following methods:

**Yaml/helm chart:**

When using the helm chart method of Smile implementation, the kafka message broker can be configured within the helm chart, please consult the  [SmileCDR Helm Chart docs](https://smilecdr-public.gitlab.io/smile-dh-helm-charts/latest/guide/smilecdr/messagebroker/) for more information on configuring Kafka within helm.

The module itself is added to the helm chart using the following structure:

```yaml
{module_id}:
    name: Channel CDA Exchange Plus
    enabled: true
    type: CHANNEL_IMPORT
    requires:
      PERSISTENCE_ALL: {persistence_module_id}
      CDA_EXCHANGE: {cda_exchange_plus_module_id}
```

Copy

**Properties file:**

```properties
module.{module_id}.type=CHANNEL_IMPORT

module.{module_id}.requires.CDA_EXCHANGE={cda_exchange_plus_module_id}
module.{module_id}.requires.PERSISTENCE_ALL={persistence_module_id}
module.{module_id}.config.channel.retry.delay_milliseconds=2000
module.{module_id}.config.channel.retry.maximum_delay_milliseconds=2000
module.{module_id}.config.channel.retry.maximum_attempts=5
module.{module_id}.config.channel.concurrent_consumers=8
module.{module_id}.config.default_mediatype=application/fhir+json
module.{module_id}.config.channel.name={channel_name}
module.{module_id}.config.channel.failed.name={failure_channel_name}
module.{module_id}.config.channel.retry.name={retry_channel_name}
module.{module_id}.config.interceptor_bean_secure_context=false
```

Copy

{channel\_name}, {failure\_channel\_name} and {retry\_channel\_name} must be filled in for the module to start correctly.

The `requires.CDA_EXCHANGE` field must be populated with the ID of the CDA Exchange Plus module for Channel Import to be correctly hooked up to the module.

**Method 3: Webadmin Console**

1. In the webadmin console, under Configuration >> Module Config >> Add Module, select Channel Import and click Add
2. Name the module (default name is: `channel_import` though it’s recommended you highlight it’s the CDA Channel Import module. Ie. cda\_channel\_import or cda\_import)
3. Fill in the properties **Channel Name**, **Retry Channel Name**, **Failure Channel Name** and under **Dependencies** choose the CDA Exchange+ module under **CDA Exchange+ module (optional)**.
4. Create Module and start.

**Configuring Camel**
**Yaml/helm chart:**

```yaml
{module_id}:
    name: Channel CDA Exchange Plus
    enabled: true
    type: CAMEL
    requires:
      PERSISTENCE_ALL: {persistence_module_id}
    config:
      routes.script.file: {file_location}
      functions.script.file: {functions_script_location}
```

Copy

**Configuration file**

```properties
module.camel.type=CAMEL

module.camel.requires.PERSISTENCE_ALL=persistence
module.camel.config.routes.script.text=
module.camel.config.interceptor_bean_secure_context=false
module.camel.config.routes.script.file={file_location}
```

Copy

**Webadmin console** Under **Configuration** >\> **Module Config** >\> **Add Module**, select **Camel** and click **Add**.

Under **Dependencies** select the persistence module of choice.

Save module config and start the module.

**Configuring the Camel routes**

Under **Camel Routes (Text)** (routes.script.text) or **Camel Routes (File)** (routes.script.file). The appropriate routes need to be set for Camel to correctly identify which broker topic it needs to be listening to and where to direct the messages from the broker towards.

Example Camel route:

```xml
<route>
    <from uri="smile:clustermgr/broker?topic=cda"/>
    <to uri="smile:camel/script?function=processKafkaMessage" />
    <to uri="smile:cda_exchange_plus/cdaToFhirPreConvertScriptProcessor"/>
    <to uri="smile:cda_exchange_plus/cdaToFhirPreConvertInterceptorProcessor"/>
    <to uri="smile:cda_exchange_plus/cdaToFhirProcessor"/>
    <to uri="smile:cda_exchange_plus/cdaToFhirPostConvertInterceptorProcessor"/>
    <to uri="smile:cda_exchange_plus/cdaToFhirPostConvertScriptProcessor"/>
    <choice>
        <when>
            <spel>#{body.isDoProcess()}</spel>
            <setBody>
                <spel>#{body.bundle}</spel>
            </setBody>
            <to uri="smile:persistence/bundleProcessor"/>
        </when>
    </choice>
</route>
```

Copy

For this given Camel route, the data from Kafka topic `cda` (clustermgr/broker) is passed through the CDA Exchange flow and stored in the persistence module with the specified partition (persistence/bundleProcessor).

Between these two events the data is passed through a Camel script `camel/script?function=processKafkaMessage` the CDA Exchange Plus processors JavaScript and Java Interceptors (`cda_exchange_plus/*` , cdaToFhirPreConvertInterceptorProcessor, cdaToFhirProcessor, cdaToFhirPostConvertInterceptorProcessor, cdaToFhirPostConvertScriptProcessor). After all conversions have taken place the data is in the format `cdaToFhirConversionResultJson` which is not accepted by the `bundleProcessor` therefore, the last part of the routes converts the body into the `IBaseBundle` format for use in `bundleProcessor`.

> Note that all pre- and post-processing processors, both JavaScript and Java are optional! If no processors are necessary these processors can be removed.

The `processKafkaMessage` processor extracts the XML string from the kafka message via the following function:

```javascript
function processKafkaMessage(string) {
    return (JSON.parse(string)).payload.payload;
}
```

Copy

Where the incoming data is assumed to be of the following format:

```json
{
	"payload": {
		"transactionId": "a0c42c2b-43e9-4e09-8cf2-be3a7b95fb19",
		"mediaType": "text/xml+cda",
		"payload": "<?xml-stylesheet type=\"text/xsl\" href=\"CCDA.xsl\" alternate=\"no\" title=\"Allscripts Default\" ?><?xml-stylesheet type=\"text/xsl\" href=\"ccda.xsl\"?><ClinicalDocument xmlns=\"urn:hl7-org:v3\" xmlns:sdtc=\"urn:hl7-org:sdtc\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><realmCode code=\"US\"/><typeId root=\"2.16.840.1.113883.1.3\" extension=\"POCD_HD000040\"/><templateId root=\"2.16.840.1.113883.10.20.22.1.8\" extension=\"2015-08-01\"/><templateId root=\"2.16.840.1.113883.10.20.22.1.8\"/><templateId root=\"2.16.840.1.113883.3.3251.1.1\"/><templateId root=\"2.16.840.1.113883.10.20.22.1.1\" extension=\"2015-08-01\"/><templateId root=\"2.16.840.1.113883.10.20.22.1.1\"/><templateId root=\"1.3.6.1.4.1.19376.1.5.3.1.5.5.1\"/><templateId root=\"1.3.6.1.4.1.19376.1.5.3.1.5.5.2\"/><templateId root=\"2.16.840.1.113883.10.20.22.1.2\"/></ClinicalDocument>"
	}
}
```

Copy

## Troubleshooting CDA Exchange Plus

CDA Exchange Plus has its own troubleshooting log that can be enabled via the `customerlib/logback-smile-custom.xml`

The following line should be uncommented

```xml
	<!-- <logger name="ca.cdr.log.cda_troubleshooting" level="DEBUG"/> -->
```

Copy

CDA Exchange Plus Import Implementation

Implementation

[CDA Exchange Plus Import Implementation](/content/docs/guide_implementation/implementation_cda_import.html)
