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 |
|
The following must be done to set up credential key "policy_user"
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:
|
||
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:
|
||
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](../_images/enrollment-product-file-uipload-invoke.png)
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 |
|
The following must be done to set up credential key "claims_user"
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:
|
||
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:
|
||
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