File Upload

Oracle Insurance Gateway supports uploading of files to the Oracle Health Insurance application. This essential means read a file, transform the file contents to the Oracle Health Insurance format, and deliver it to the Oracle Health Insurance application. This file upload feature can be used to maintain configuration or operational data within Oracle Health Insurance applications by setting up integrations which in turn make use of the Oracle Health Insurance application capabilities, like

1) Generic API e.g. POST/PUT/PATCH on a resource,

2) Activity based File Import feature.

3) Specific IPs e.g. Group client IP, Claims In IP.

To get an idea on the possibilities around Integration use cases, consider the following use cases

  • Enrollment product upload - In this example, a record delimited file gets read and gets transformed to JSON request(s). Through Generic API POST/PUT on /enrollmentproducts resource enrollment products can be created or updated in Policies application. For this use case, a TRANSFORM step is used to read the file, and send a POST or PUT request to create /update enrollment products

This is not recommended for high volume files.
  • Provider upload - In this example data, a record delimited file gets read and gets transformed to the Oracle Health Insurance-specific XML file. The XML file is then uploaded as a data file to the Claims application. As a next step, Activity Provider Import gets triggered to process the providers in the Claims application. This use case can be implemented by having a TRANSFORM step followed by an ACTIVITY step

  • Group upload - In this example data is read from a CSV file and is transformed into a Group Configuration integration point JSON request. Using the Group Configuration integration point, the group client, and group accounts are created in the Policies application. This use case can be implemented by having a TRANSFORM step

The Oracle Insurance Gateway configuration for all the examples in this chapter is shown using API/IPs. The same is also possible using Oracle Insurance Gateway user interface.
The examples below assume that the reader is familiar with Oracle Health Insurance applications integration capabilities (especially HTTP API/IP concepts). For details refer to chapters on HTTP API/IP concepts in the Developer Guide.
The examples use transformation dynamic logic to transform the file data into format as needed by Oracle Health Insurance applications. For more details on transformation possibilities refer to chapter Transformation Dynamic Logic in the guide

Enrollment Product Upload

The first step is to identify the Destinations. Here, Policies is the interfacing application with the Oracle Insurance Gateway application. Oracle Insurance Gateway needs to invoke methods POST/PUT on /enrollmentproducts generic API to create or update enrollment products in Policies. Therefore, for this use case a REST destination to communicate with Policies must be set up.

Configure the rest destination "policies" as follows:

Fields Configuration Remarks

code

policies

credentialKey

policy_user

The following must be done to set up credential key "policy_user"

  1. Create credential using credential IP or Credential Page

    Example: To set up credential key "policy_user" using Credential IP the following must be done

    PUT  http(s)://{host:port}/{oig application context}/credentials/credential/_policy_user_

    with payload:

    {
      "username" : "user that OIG should use to access policy application",
      "password": "password to access policy application"
    }
  2. Set the authentication mechanism using generic api - properties or using property configuration page

Example: To set up authentication using Generic API - /properties (assuming the polices application uses basic authentication) the following must be done

POST  http(s)://{host:port}/{oig application context}/generic/properties

with payload:

{
    "name": "ohi.service.**policy_user**.client.authentication",
    "value": "BasicAuthentication"
}
For details on Generic API, refer to section HTTP API concepts of the Developer Guide.

addressKey

address.key.policies.baseurl

The address key can be setup using Generic API /properties or properties page

Example : To set up address key using Generic API - /properties the following must be done

POST:  http(s)://{host:port}/{oig application context}/generic/properties

with payload:

{

  "name": "address.key.policies.baseurl",
  "value": "URL till policiesComponent context root, e.g. http"://{host"port}/{context-root}
}

destinationType

REST

typeConfig

path: {path}

httpMethod: {httpMethod}

The path and http method are parameterized (recommended). This way a single rest destination can be used by multiple integrations with Policies application.

NOTE: for the given use case, the path and http method configuration will be driven by the "Callout" rule. See dynamic logic below for clarification.

An example of the JSON payload for the creation of polices destination using API /restdestinations is given below.

{
    "code": "policies",
    "credentialKey": "policy_user",
    "addressKey": "address.key.policies.baseurl",
    "destinationType": "REST",
    "typeConfig": {
        "path": "{path}",
        "httpMethod": "{httpMethod}"
    }
}

The next step is to set up the dynamic logic - data transformation.

Suppose, if the file that needs to be uploaded has the following structure:

CODE;NAME;PLAN_NUMBER;AMT_DIST;PPR
DENTAL;Dental;DENTAL_BASIC,D,D
PPO;PPO;PPO_BASIC,D,D

then the logic would look something like

import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import org.apache.commons.csv.*
import javax.ws.rs.core.Response;

trigger.dataFiles.each {
    df ->
        char delimiter = ';'
        def csvParsers = fileReader.csvParser(df, CSVFormat.DEFAULT.withFirstRecordAsHeader().withIgnoreHeaderCase().withTrim().withDelimiter(delimiter))
        csvParsers.each { csvRecord ->

            def jsonBuilder = new JsonBuilder()
            def enrollmentProduct = new JsonBuilder()
            def enrollmentProductexists = false
            String enrollmentProductID = ""
            def jsonPayload = JsonOutput.toJson([
              resource: [q: "code.eq(" + csvRecord['CODE'] + ")"]
            ])

            Response enrollmentProductResponse = webTarget("policies")
                .path("generic").path("enrollmentproducts/search")
                .request()
                .buildPost(Entity.json(jsonPayload))
                .invoke()

            if (enrollmentProductResponse.status == 200) {
                enrollmentProductID = (new JsonSlurper()
                        .parseText(enrollmentProductResponse.readEntity(String.class)))
                        .items[0].id
                enrollmentProductexists = true;
            }

            def products = new ArrayList()
            def product = {
                product {
                    code csvRecord["PLAN_NUMBER"]
                }

            }
            products.add(product)

            enrollmentProduct {
                amountDistribution csvRecord["AMT_DIST"]
                descr csvRecord["NAME"]
                code csvRecord["CODE"]
                displayName csvRecord["NAME"]
                partialPeriodResolution csvRecord["PPR"]
                insuranceType {
                    code "HEALTH_INSURANCE"
                }
                currencyParameter {
                    code "USD"
                }
                currencyPremium {
                    code "USD"
                }
                enrollmentProductDetailList(jsonBuilder.call(products))
            }

            if (enrollmentProductexists) {
                enrollmentUploadResponseSt = webTarget("policies")
                        .path("generic").path("enrollmentproducts").path(enrollmentProductID)
                        .request()
                        .header("Accept", "application/json")
                        .buildPut(Entity.json(enrollmentProduct.toString()))
                        .invoke()
                        .readEntity(String.class);

            } else {
                enrollmentUploadResponseSt = webTarget("policies")
                        .path("generic").path("enrollmentproducts")
                        .request()
                        .header("Accept", "application/json")
                        .buildPost(Entity.json(enrollmentProduct.toString()))
                        .invoke()
                        .readEntity(String.class);
            }

            exchangeStep.addLogEntry("Enrollment Product Created/Updated: "
                    + new JsonSlurper().parseText(enrollmentUploadResponseSt).code)
        }

}

The dynamic logic can be created by sending in POST request on /generic/dynamiclogic (generic resource)

{
    "code": "enrollmentProductFileupload",
    "active": true,
    "descr": "Enrollment Product Upload",
    "subtype": "FUNC",
    "logic": " groovy logic",
     "signature": {
        "name": "Data Transformation Payload"
       }
}

The next step is to create an Integration using the building blocks mentioned above. This is done by sending the following request using POST Method to Generic API resource /integrations.

{
    "code": "enrollmentProductFileupload",
    "type": "integration",
    "descr": "Enrollment Product Upload",
    "integrationSteps": [
        {
            "code": "enrollmentProductFileupload",
            "sequence": 1,
            "subtype":"TRANSFORM",
            "outputName":"enrollmentProductFileupload",
            "functionTransformation": {
              "code" :"enrollmentProductFileupload"
            }
        }
    ]
}

Exchange Integration Point - With the File option can be used to invoke this integration. The image below shows integration invocation using a REST client.

enrollment-product-file-uipload-invoke

Provider Upload

In this use case, Oracle Insurance Gateway needs to communicate with Claims application for two things 1) update data file and 2) invoke provider import activity. Oracle Insurance Gateway provides out of the box capability to upload data files to Oracle Health Insurance applications. This mechanism gets triggered when the system sees the delivery destination is of type "FILEUPLOAD". For this use case two destinations must be set up 1) FILEUPLOAD destination "claims_fileUpload" and 2) REST destination "claims"

The FILEUPLOAD destination "claims_fileUpload" is configured as :

Fields Configuration Remarks

code

claims_fileUpload

credentialKey

claims_user

The following must be done to set up credential key "claims_user"

  1. Create credential using credential IP or Credential Page.

    Example: To set up credential key "claims_user" using Credential IP the following must be done

    PUT:  http(s)://{host:port}/{oig application context}/credentials/credential/_claims_user_

    with payload:

    {
      "username" : "user that OIG should use to access claims application - data file set IP",
      "password": "password"
    }
  2. Set the authentication mechanism using generic api - properties or using property configuration page.

Example: To set up authentication using Generic API - /properties (assuming the polices application uses basic authentication) the following must be done

POST : http(s)://{host:port}/{oig application context}/generic/properties

with payload:

{
    "name": "ohi.service.**claims_user**.client.authentication",
    "value": "BasicAuthentication"
}
For details on generic api refer to Developer Guide, section HTTP API concepts.

addressKey

address.key.claims.baseurl

The address key can be setup using generic api /properties or properties page

Example : To set up address key using generic api - /properties the following must be done

POST  http(s)://{host:port}/{oig application context}/generic/properties

with payload:

{

  "name": "address.key.claims.baseurl",
  "value": "URL till {claimsComponent} context root
   e.g. http://{host"port}/{context-root}"
}

destinationType

FILEUPLOAD

The configuration of the rest destination "claims" is similar to "policies" rest destination

The next step is to configure dynamic logic to transform CSV file to the Oracle Health Insurance provider XML structure. Suppose, if the file that needs to be uploaded has the following structure:

Provider No;Therapy Code;Commencement Date;Suspended date;Expelled Date;Surname;First Name;Second Name;Title;Address Line 1;[..more columns..]
T38407R;REM;15-3-2019;;;-;Jane;;Ms;unit 10/112 Wellington Street;;;SUBIACO;WA;6155;0422 866 212;0422 221 676;X
AW03812R;REM;13-3-2019;14-3-2019;;ABBOTT;Bryan;;Mr;Suite 801, Level 2, 151;Carlton Street;;NEWCITY;NSW;2000;02 9264 3321;0431 443 950;

then the dynamic logic "providerFile_transform" would look something like:

import org.apache.commons.csv.*
import javax.ws.rs.core.Response
import groovy.json.JsonSlurper
import groovy.xml.StreamingMarkupBuilder

// Note: this example assumes a single datafile in a set
trigger.dataFiles.each { df ->
    // define the CSV structure that is capable of parsing the csv input
    char delimiter = ';'
    def csvParser = fileReader.csvParser(df, CSVFormat.DEFAULT
            .withFirstRecordAsHeader()
            .withIgnoreHeaderCase()
            .withTrim()
            .withDelimiter(delimiter))
    // this closure defines the XML payload
    def individualProvidersXML = { xml ->
        individualProviders {
            csvParser.each { csvRecord ->

                // variables that we will be reusing
                incomingProviderCode = csvRecord["Provider No"]
                startDate = formatDate(csvRecord["Commencement Date"])
                endDate = formatDate(csvRecord["Suspended date"])

                def jsonPayload = JsonOutput.toJson([
                 resource: [q: "code.eq('" + incomingProviderCode + "')"],
                     resourceRepresentation: [expand: "all"]
                 ])
               // get existing subscriptionRecords
               // maybe add accept header too for dynamic data

              Response providerResponse = webTarget("claims")
                .path("generic")
                .path("individualproviders/search")
                .request()
                .accept("application/json")
                .buildPost(Entity.json(jsonPayload)
                  .invoke()

                def existingSubscriptions = []
                if (providerResponse.status == 200) {
                    existProvider = new JsonSlurper().parseText(providerResponse.readEntity(String.class))
                    existingSubscriptions = existProvider.items[0].subscriptions
                }

                // xml element for the individual provider
                individualProvider(
                        code: incomingProviderCode,
                        flexCodeDefinitionCode: "PROVIDER",
                        startDate: startDate,
                        endDate: endDate,
                        titleCode: csvRecord["Title"],
                        name: csvRecord["Surname"],
                        firstName: csvRecord["First Name"],
                        outputLanguageCode: "en",
                        nameFormatCode: "NAFMDFLTPROV"
                ) {
                    renderingAddressList {
                        renderingAddress(startDate: startDate) {
                            serviceAddress(
                                    street: csvRecord["Address Line 1"]?.toUpperCase(),
                                    city: csvRecord["City/Suburb"],
                                    postalCode: csvRecord["Postcode"],
                                    countryCode: "US",
                                    countryRegionCode: "RI",
                                    phoneNumberBusiness: csvRecord["Phone Number"]
                            )
                        }
                    }
                    subscriptions {
                        existingSubscriptions.each { s ->
                            record(commencementDate: s["commencementDate"],
                                    suspendedDate: s["suspendedDate"],
                                    expelledDate: s["expelledDate"]
                            )
                        }
                        record(commencementDate: startDate,
                                suspendedDate: endDate,
                                expelledDate: formatDate(csvRecord["Expelled Date"])
                        )
                    }
                }
            }
        }
    }

    def builder = new StreamingMarkupBuilder()
    builder.encoding = "UTF-8"
    outputWriter << builder.bind(individualProvidersXML)

}

// formats the dateString to a regular date, or returns null/empty string
String formatDate(String dateString) {
    if (dateString?.trim()) {
        return Date.parse("dd-MM-yyyy", dateString).format("yyyy-MM-dd")
    } else {
        return dateString
    }
}

In the dynamic logic, a check is made to lookup for the provider in the Oracle Health Insurance application. If the provider exits with the specified "Provider No" in the Oracle Health Insurance application then the dynamic record subscriptionRecords is updated with additional details from the file. Everything else is overwritten by the information from the file.

Depending on the use case at hand, various possibilities can be realized by making appropriate changes to the dynamic logic, to name a few 1)Overwrite completely with the details from file 2)Overwrite select few (as shown in the example), and 3)No overwrites, only add if missing and so on.

The next step is to create an Integration using the building blocks mentioned above. This is done by sending the following request using POST Method to Generic API resource /integrations.

{
  "code": "provider_import_invocation",
  "type": "integration",
  "descr": "Provider Import Invocation With Notification",
  "integrationSteps": [
    {
      "code": "file_upload_step",
      "sequence": 1,
      "subtype": "DELIVERY",
      "destination": {
        "code": "claims_fileUPload"
      },
      "functionTransformation": {
        "code": "providerFile_transform"
      }
    },
    {
      "code": "invoke_provider_import",
      "sequence": 2,
      "subtype": "ACTIVITY",
      "indicatorExpectNotification": "true",
      "outputName": "invoke_provider_import",
      "typeConfig": {
        "code": "PROVIDER_IMPORT",
        "level": "GL",
        "description": "Provider Import",
        "parameters": [
          {
            "name": "dataFileSetCode",
            "value": "{dataFileSetCode}"
          },
          {
            "name": "responseDataFileSetCode",
            "value": "{responseDataFileSetCode}"
          }
        ]
      },
      "destination": {
        "code": "claims"
      }
    }
  ]
}

Here, indicatorExpectNotification is set to true, this would put the exchange into waiting mode, until the activity concludes and informs Oracle Insurance Gateway that the import is successful or not.

Additional configuration is needed for Claims to come back with the notification to Oracle Insurance Gateway. To do this, the system property ohi.activityprocessing.notification.endpoint.PROVIDER_IMPORT in Claims must be set to the notification endpoint in Oracle Insurance Gateway:

http(s)://{host:port}/{oig application context}/notification

Also, in Claims application, the credential and authentication property must be set for the key "PROVIDER_IMPORT". This is similar to the setting of credential and authentication for the key policies_user as explained in the enrollment product upload example

Another thing to note here is, that, this example uses bind parameters for dataFileSetCode and responseDataFileSetCode. The value for these are provided by exchange properties. In the configuration of any step, the exchange properties can be used as bind parameters. For more details on properties, refer to Gateway Dynamic Logic Bindings

Exchange property by the name "dataFileSetCode" is set by the system when a delivery step in combination to destination type file upload is used to upload a file in the Oracle Health Insurance application. The value of the property is set to the data file set code under which the file is uploaded by the system to the Oracle Health Insurance application.

Value for "description" and "responseDataFileSetCode" can either be 1) input parameters at the time of invocation or 2) properties added in the dynamic logic function post process of the DELIVERY step.

Exchange Integration Point - With the File option can be used to invoke this integration.

Group Upload

The group upload is similar to enrollment product upload, here the group file gets transformed using transformation dynamic logic to Group Configuration IP JSON request format. Using the webTarget predefined method the request can be sent to Group Configuration IP endpoint.

 String groupRS = webTarget("policies")
                    .path("groupclients")
                    .request()
                    .header("Accept", "application/json")
                    .buildPut(Entity.json(groupReq.toString()))
                    .invoke()
                     .readEntity(String.class);

// groupReq holds the {ohi} JSON format as specified by the group configuration IP