Functions
A dynamic logic function is the most generic type of dynamic logic: it can have one or more input arguments and return different types of output. A dynamic logic function is used to extend default behavior in certain areas. Each usage of dynamic logic functions is described in a separate paragraph below.
Data Collection Custom
Data can be collected from various sources in different ways. One of the supported ways is a custom dynamic logic. The following signature applies:
In or Out | Name | Type | Description |
---|---|---|---|
In |
|||
In |
fileReader |
DataFileReader |
If there is a need to read files that were output of previous steps, then this object is available to fetch readers for the input files. The available types are:
|
Out |
n/a |
String |
Result of the custom logic. |
This signature allows for making external service callouts.
Examples
This example creates XML payload with integration code. Then it calls out an external service with the payload.
The result of the output is passed back as the map.
import groovy.json.JsonSlurper
/* Trigger the Get Request and set the query parameter to manipulate the response*/
String response = webTarget("custom_destination")
.path("customData")
.queryParam("resultType", properties["resultType"])
.request().get()
.readEntity(String.class)
def responseObject = new JsonSlurper().parseText(response)
/* Add to properties if the next step is run or not based on the response */
properties.put("runExchangeStep",responseObject.isValid)
/* Create the XML Response with the result returned in the response if there is result, else return null*/
if(responseObject.result) {
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.response(code:responseObject.result)
return writer.toString()
}
return null
For more details on external callouts refer chapter External Callout in the guide |
To disable automatic redirect and to avoid 303 response code in a POST call, include the property jersey.config.client.followRedirects: Response response = webTarget("custom_destination") .path("customData") .request() .property("jersey.config.client.followRedirects", Boolean.FALSE) .build("POST") .invoke() |
Data Transformation Payload
Data collected from the source systems may need to be transformed. The transformation is supported using dynamic logic. The following signature applies:
In or Out | Name | Type | Description |
---|---|---|---|
In |
|||
In |
outputWriters |
java.io.Writer |
An outputWriter writes data to a destination data file in a dynamic logic. You can access the outputWriter by calling the outputWriters.getDataFileOutputWriter() method. The method creates a data file set for each transformation. The outputWriters.getDataFileOutputWriter("file name", "application/json") method creates a datafile with the name, "filename", within a data file set. Other writers write to other file types, such as
|
In |
fileReader |
DataFileReader |
If the transformation is for type file, then this object is available to fetch readers for the input files. The available types are:
|
Out |
n/a |
String |
Optional. Result from the transformation. The transformation logic may also use pre-defined methods to write out to a file. |
Examples
-
Data File Set Transformation: Example to transform DataFileSet retrieved from policies as an extract, the output name is extract and transforming it to flat lines.
import com.google.gson.*
import com.google.gson.stream.JsonReader
def outputWriter = outputWriters
.getDataFileOutputWriter()
.lineWriter()
int countOfRecords = 0
extract.getDataFiles().each { df ->
def reader = fileReader.jsonReader(df)
Gson gson = new GsonBuilder().create()
// Read file in stream mode
reader.beginArray()
while (reader.hasNext()) {
// Read data into object in a stream fashion
def policy = gson.fromJson(reader,Map.class)
def transformedResult = []
++countOfRecords
transformedResult.add(policy.code + " transformed")
policy.policyEnrollmentList.each { pe ->
transformedResult.add(pe.person.code)
transformedResult.add(pe.person.dateOfBirth)
transformedResult.add(pe.person.gender)
pe.person.addressList.each { a ->
transformedResult.add(a.city)
transformedResult.add(a.houseNumber)
transformedResult.add(a.street)
}
pe.policyEnrollmentProductList.each { pep ->
transformedResult.add(pep.startDate)
}
}
policy.policyStatusHistoryList.each { ps ->
transformedResult.add(ps.gid)
transformedResult.add(ps.status)
transformedResult.add(ps.dateTime.value)
}
policy.policyholderList.each { ph ->
transformedResult.add(ph.startDate)
transformedResult.add(ph.endDate)
}
transformedResult.add("Other Versions: "+policy.policies.size())
transformedResult.add(policy.attachedpolicydata.manual)
outputWriter.write(transformedResult.join(", "))
outputWriter.write("\n")
}
reader.endArray()
}
outputWriter.write("Number of Records: "+countOfRecords)
-
Raw Payload Transformation: Fetched data from Claims application, and returning another JSON payload to determine if the claim is APPROVED, if the amount is more than 0 and does not have Fatal messages, else it is DENIED.
import groovy.json.JsonSlurper
import groovy.json.JsonOutput
def json = " "
if (!binding.hasVariable("claimsData")) {
println("missing parameter is not present within request.")
json = JsonOutput.toJson([code: "No Claim Data Present"])
} else {
def claimObject = new JsonSlurper().parseText(claimsData)
def claimStatus= claimObject.amount > 0 ? "APPROVED": "DENIED"
if(claimStatus=="APPROVED") {
def containsFatalMessages = claimObject.claimMessages.any{it.severity=="F"}
claimStatus = containsFatalMessages ? "DENIED" : "APPROVED"
}
json = JsonOutput.toJson([code: claimObject.code, type: claimObject.type, claimStatus: claimStatus])
}
return json
For more details on Transformation refer to chapter Transformation Dynamic Logic in the guide |
Error Payload
It is helpful in constructing any technical error into a payload in order to invoke an external workflow or to notify an external system. The following signature applies:
In or Out | Name | Type | Description |
---|---|---|---|
In |
|||
In |
exception |
Exception |
Exception that failed the exchange step and hence triggered this function. In some situations, external workflow can be invoked through localized integrations, whilst not failing the exchange (step). In that case the exception holds information about the functional issue that was observed. In case of exchange time out, the exception holds information about the integration or the integration step that timed out. |
In |
fileReader |
DataFileReader |
If there is a need to read files that were output of previous steps, then this object is available to fetch readers for the input files. The available types are:
|
Out |
String |
JSON Payload that needs to be sent out. |
Example: The following function specifies logic to create JSON request to invoke a workflow when integration invocation fails
JSON
{ "type":"",
"errors":[{
"errorCode":"EXT_001",
"errorText":"<error message>"
}]
}
Dynamic logic:
import groovy.json.JsonBuilder
def jsonBuilder = new JsonBuilder()
def errorsJsonBuilder= new JsonBuilder()
def errorArrayList= new ArrayList()
def errors = {
error(
errorCode: "EXT_001",
errorText: exception.message
)
}
errorArrayList.add(errors)
errorsJsonBuilder {
type : exchangeStep.exchange.integration.code
errorList(jsonBuilder.call(errorArrayList))
}
return errorsJsonBuilder.toString()
Extension
This is a common signature, available in all the applications. This signature has no bindings. It allows for writing extensions to the dynamic logic scripts that are delivered by the product.
A dynamic logic function of signature Extension should be accompanied with a reference to the base dynamic logic and vice-versa (OHI-DYLO-031). The referenced base dynamic logic function cannot be of signature Reusable Code or Extension (OHI-DYLO-030).
Invoke Function Payload
This is used in PROCESS type of integration step to return a payload to be used to invoke the process. The following signature applies:
In or Out | Name | Type | Description |
---|---|---|---|
In |
|||
In |
fileReader |
DataFileReader |
If there is a need to read files that were output of previous steps, then this object is available to fetch readers for the input files. The available types are:
|
Out |
String |
Payload that needs to be sent out for Process |
Example: An integration to needs to invoke a long-running process in an end application, and it expects XML payload.
Example dynamic logic:
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.process(
description : "Registration set 001"
)
return writer.toString()
Parameter Extraction
If the integration invocation is through a payload that contains input parameters for the steps, this function helps in extracting those input parameters. The following signature applies:
In or Out | Name | Type | Description |
---|---|---|---|
In |
|||
In |
integration |
Integration |
The Integration configuration that references the Parameter Extraction Dynamic Logic. |
In |
headers |
HeadersObject |
A custom object supporting Header Extraction (see next section) |
Out |
Not Applicable |
Map |
Result from the extraction of parameters from input payload. |
Example: Invoke an integration through a message with this payload:
<group name="ACME">
<member>
<id>12</id>
<name>Scott</name>
</member>
</group>
For example, a group name needs a member ID as input parameters for the data collection. The parameter extraction dynamic logic can be configured as shown below to extract the input parameters.
def rootNode = new XmlSlurper().parseText(trigger)
return [groupName:rootNode.@name.text(), memberId:rootNode.member.id]
Header Extraction
By default all HTTP Headers, excluding the security sensitive HTTP Headers, are made available as Exchange properties implicitly through Header Extraction. This way an HTTP Header can be used throughout the steps of an Exchange.
Additionally, it is possible to set up Parameter Extraction dynamic logic with the means to, selectively mark (a subset of) these headers as response headers using predefined methods available on the header binding. These response headers are then automatically included when calling an external service or when returning the response for an Exchange that is executed in a synchronous/blocking fashion. As an example in the below code snippet, all HTTP Headers that start with 'hiconline' are marked as response headers that need to go out on the Exchange using a predefined method addResponseHeaders that takes in a Predicate.
import java.util.function.*
Predicate isEclipseHeader = { a ->
a.key.startsWith("hiconline")
}
headers.addResponseHeaders(isEclipseHeader)
Additionally, a number of signatures are possible to add response headers using the Headers object.
Operation | Description | Example |
---|---|---|
addResponseHeader(String key) |
Mark a request header by key as a response header |
addResponseHeader("request-header-one") |
addResponseHeaders(String…keys) |
Mark a collection of request headers by their keys as response headers |
addResponseHeaders("request-header-on", "request-header-two") |
addResponseHeaders(Predicate<Map.Entry<String,?>>) |
Mark a collection of request headers by a predicate as response headers |
see the above example with isEclipseHeader predicate |
Please note that HTTP Headers potentially can have multiple values per HTTP Header key. With respect to Header Extraction, only the first value is extracted in such a case. |
Notification
Structure to store notification details. Following methods are available through the notification object to add more context.
-
notification.status or notification.setStatus(String): The string value can either be 'Failure' or Success'.
-
addLink(String rel, String href): To add links on the notification
See above for example.
Notification Payload Extractor
If the integration invokes a process that wishes to resume the exchange after completion. It can do so by sending a notification to the gateway with the relevant correlationId in its header or as part of the notification payload.
In case the notification payload is the specific format that the Notification Integration Point accepts, the Integration Gateway framework reads the notification and resume the process.
It is, however also possible to resume the exchange using a custom payload, for example, a claim event. In case it is a custom format, this signature can be used to extract notification payload from the custom payload.
This logic is configured on the process step that wishes to resume the exchange with custom payload.
In or Out | Name | Type | Description |
---|---|---|---|
In |
|||
In |
notificationPayload |
String |
The custom payload that resumes the exchange through notification endpoint. |
In |
notification |
Notification |
Notification Object to contain the notification specific details, like status, links for data collection, messages and recovery. |
Example: the exchange is invoked with a claim status response containing links:
{
"status" : "FINALIZED",
"links" : [{
"rel": "claimmessages",
"href": "http://[hostName]:[portNumber]/[api-context-root]/claimmessages"
}
]
}
The notification payload extractor dynamic logic configured in the Process Step configuration is invoked to extract the Notification object. Example dynamic logic:
import groovy.json.JsonSlurper
def claimObject = new JsonSlurper().parseText(notificationPayload)
def recoveryLink = false
if(claimObject.links != null) {
for(int ctr=0;ctr<claimObject.links.size();++ctr) {
def rel = claimObject.links[ctr].rel
if(rel.contains("messages")) {
rel = "messages"
}
if(rel.contains("recovery")) {
recoveryLink = true
}
notification.addLink(rel, claimObject.links[ctr].href)
}
}
if(claimObject.status==null) {
notification.status= recoveryLink ? "Failure" : "Success" } else {
notification.status = (claimObject.status == "FINALIZED") ? "Success": "Failure"
}
Reusable Code
This is a common signature, available in all the applications. This signature has no bindings. It allows for writing standalone methods/reusable classes code that can be shared across other scripts.
If a reusable dynamic logic is changed, and the changes are not reflected in the dynamic logics that have used or imported the reusable dynamic logic. To get it to work, invoke the invalidateall operation before starting other operations. See Invalidate Dynamic Logic
for more info.
|
Step Post Process
It is helpful in extracting relevant pieces of information from the just executed step to be used in the next step. The following signature applies:
In or Out | Name | Type | Description |
---|---|---|---|
In |
|||
In |
fileReader |
DataFileReader |
If there is a need to read files that were output of previous steps, then this object is available to fetch readers for the input files. The available types are:
|
Out |
properties |
Map |
Updated properties collection containing properties that are extracted from the step’s output. Can also return null or empty map if there are no properties to update. |
Example: The integration step queries claims for external claims data based on claim code and receives the following payload. The step’s output name is ecdData. The next step is to update the external claims data. For that it is required to extract the external claims data id to make the next step possible.
{
"id": "484632",
"settlementReason": {
"id": "1",
"links": [
{
"href": "<<environment>>/api/generic/settlementreasons/1",
"rel": "canonical"
}
]
},
"claim": {
"id": "471238"
}
}
Example dynamic logic:
return ["external_claims_data_id":new JsonSlurper().parseText(ecdData).id]
Subflow
It is used to provide list of parameters for invoking subflow. The size of the list of parameters is the number of times the subflow is triggered. The following signature applies:
In or Out | Name | Type | Description |
---|---|---|---|
In |
|||
In |
exception |
Exception |
Exception that failed the exchange step and hence triggered this function. |
In |
fileReader |
DataFileReader |
If there is a need to read files that were output of previous steps, then this object is available to fetch readers for the input files. The available types are:
|
In or Out |
subflowStepParameters |
SubflowStepParameters |
List of "request parameters" map with which a sub exchanges get triggered. |
Subflow Step Parameters
It provides a structure to store a list of Map of request parameters. The method addProperties can be used to add request parameters required for a triggering a single subflow.
-
addProperties(Map<String,Object> properties): The map of properties for subflow invocation.
The order in which the method addProperties get called on the subflowStepParameters object determines the order in which the subflow gets triggered for sequential the processing.
Example:
Adding parameters for invoking the subflow twice, once with file ProviderImportInsert.csv and other with file ProviderImportUpdate.csv.
subflowStepParameters.addProperties([dataFileCode:"ProviderImportInsert.csv"])
subflowStepParameters.addProperties([dataFileCode:"ProviderImportUpdate.csv"])
Example:
This below code results in the invocation of four subflows with groupAccountCode as request parameter.
subflowStepParameters.addProperties([groupAccountCode:"ABC"])
subflowStepParameters.addProperties([groupAccountCode:"DEF"])
subflowStepParameters.addProperties([groupAccountCode:"GHI"])
subflowStepParameters.addProperties([groupAccountCode:"JKL"])
Test Unit
A dynamic logic with the Test Unit signature tests another dynamic logic. Such a dynamic logic that runs a test on another dynamic logic is called a test case. A test case sends objects as input to the unit under test (UUT) and validates the output.
In or Out | Name | Type | Description |
---|---|---|---|
Out |
Not Available |
Boolean |
Returns |
You can run a test case using the Test Dynamic Logic Integration Point. |