1.0 Overview
Message
Enrichment is a common use case for Integration Software. A scenario for
message enrichment is depicted at Figure 1.0.
Figure 1.0
There
is an incoming payload that must be enriched with certain data before the
integration system can pass it on further down the processing chain to an
outbound endpoint. The message enricher pattern is depicted states that the
original inbound payload is to be retained, but the enriched message is
attached as an additional payload before being relayed to an outbound endpoint.
MuleSoft offers this support via the message enricher scope. That
is all fine but what if we want to modify the original payload. The following
sections are lab session showing the users how this can be done.
2.0 Hypothetical Scenario
Figure 2.0
Lets
say you are an integration developer and have been challenged to build an
integration scenario depicted in figure 2.0.
At
Figure 2.0 you have to build an integration module that accepts a inbound
payload (1), you will need to fire a call to an external web
api (2) to get additional data, so that you could enrich the
original inbound payload with the acquired data (3), and further
pass it on down to an outbound endpoint (4).
Lets
put some meat on this hypothetical scenario, lets say the expected inbound
payload is a list of international orders in JSON format like the
following (1).
2.1 Inbound Payload
{
"order":[
{
"country" : "New
Zealand",
"item" :{
"currency":"$",
"name":"Shovel",
"qty":"2",
"unitPrice" : "10"
}
},
{
"country" :
"Malaysia",
"item" :{
"currency":"$",
"name":"Satay",
"qty":"2",
"unitPrice" : "10"
}
}
]
}
|
2.2 Additional Enrichment Data
And
when the message enrichment processor makes a call to the external web
API (2), the data returned by this external web API is as per the
following:
{
"Currency": [
{"Country":"New
Zealand","CurrencyCode":"NZD",
"CurrencyDesc":"New Zealand Dollars"},
{"Country":"Malaysia",
"CurrencyCode":"MYR",
"CurrencyDesc":"Malaysia Ringgit"},
{"Country":"Singapore",
"CurrencyCode":"SGD",
"CurrencyDesc":"Singapore Dollars"},
]
}
|
2.3 Outbound Payload
The
Mule' message enricher must take this additional data and selected the correct
currency code for all the orders located in inbound payload (3),
and as a result of the enrichment operation the payload would be modified to
the following.
{
"order": [
{
"country": "New
Zealand",
"item": {
"currency":
"NZD",
"name": "Shovel",
"qty": "2",
"unitPrice": "10"
}
},
{
"country":
"Malaysia",
"item": {
"currency":
"MYR",
"name": "Satay",
"qty": "2",
"unitPrice": "10"
}
}
]
}
|
The
orders in the inbound payload with the "currency" value of
"$" is replaced with its respective country's currency
code. As described earlier this is a hypothetical scenario but I believe
that this scenario will always occur in either one incarnation or another.
Section 3.0 will show how this can be done via MuleSoft, so let’s dive in.
3.0 Lab Session
The
following picture shows the end result of building of a Mule flow that is capable
of processing inbound payload as described in section 2.0.
Figure 3.0
The
mule flow is numbered with execution step sequence, this is so that it would be
easier for me to walk readers through the processing mechanics of it all.
new
com.fasterxml.jackson.databind.ObjectMapper().readValue(flowVars.currencyCode,
java.util.HashMap)
|
def
curArrayList = flowVars.JavaCurrencyCode.get("Currency");
for
(i = 0; i <curArrayList.size(); i++) {
def item = curArrayList.get(i);
System.out.println(item.get("Country"));
if(payload.country.equals(item.get("Country"))){
payload.item.currency =
item.get("CurrencyCode")
break;
}
}
|
At
Figure 3.0, you will be able to see two flows, the main flow is called
"messageenrichersFlow" this flow is an implementation of the
"Message enricher" pattern. The second flow
"ExternalWebAPI" flow is used to simulate a call to an external
system and returning a payload of additional data to be populated to the
original inbound data.
At (1) and
inbound JSON payload (as described in 2.1) is received by
"messageenrichersFlow", at (2) we need to
convert the JSON payload to a Java object so that it could be easily
manipulated via the "Mule
Expression Language". (3) is an implementation of the
first message
enricher scope with the name of "Msg Enricher: Getting List Of
Currency", here I have simulated a call to an external system via a VM
endpoint, when this called is relayed to (3.1.1), it will reply
back to the main flow with a hardcoded payload at (3.1.2) (the
content of the hardcoded payload is as per described in section 2.2).
The
currency list returned (section 2.2) is in JSON format I need to convert it
into a Java object so that I could programmatically access it for further
processing. We cannot use the "JSON to Object" transformer here as
it's implicit usage is to transform a message payload, the additional currency
list returned from the (3.1.1) is not in the message context
of "messageenrichersFlow". The JSON message that is returned
is stored in a flow variable "currencyCode" as depicted by
Figure 3.1 below.
Figure 3.1
In
order to convert the content of flow variable "currencyCode" in
to a java collection object we have to create another message enricher (4),
in this new message enricher we will use the expression transformer to
transform the JSON string to a Java HashMap. The following is the code snippet
that is being used in the Expression transformer.
I made
use of the Jackson
Data bind api to convert the JSON data (in section 2.2) to a Java
Hashmap, as a result of executing the Java code the JSON data will be converted
to the following Java Object depicted in Figure 3.2 below.
Figure 3.2
This
new Java object is then stored in a flow variable called
"JavaCurrencyCode".
At number (5) I
made use of the "For
Each" scope to loop through the inbound payload order by
order. I have made use of the Groovy script component (5.1) to
implement an inner loop to traverse through he Currency List Java Hashmap, this
is so that I could select the correct currency code for the order being inspected,
we can’t use the Mule's "For
Each" scope as an implementation for the inner loop because it
is not capable of executing the break mechanism, I want to be able to break the
inner loop when the matching country is found, Groovy scripting is the only
option for me (you could also user other script language i.e. Ruby). The
following are the Groovy script used.
I just
need you to notice that in the groovy script you can reference mule objects
directly, for instance line number 5 and 6, you would usually access the
payload object via MEL by enclosing it in the following format #[...]. Here in
Groovy script you are able to access it directly, how cool is that? :)
After
number (5) the payload is then transformed back to JSON format
so that it could be passed on to the other endpoints. The following postman
print screen shows the inbound JSON data that is being passed in to the Mule
application and the resulting outbound JSON that is retuned with all it's
currency code being modified.
4.0 Conclusion
While
building the integration module for the mentioned hypothetical scenario I have
made a few observation I would like to share, please feel free to give me some
feedback and comments pertaining to the observations that I have made.
- Message
Enricher Scope -
We can only have 1 operation in a message enricher scope, we cant nest
additional message processor in a Message enricher scope for this reason I
have created two separate message enricher scope (Figure 3.0 (3) & (4)).
- For
Each Scope -
Mule's For Each scope is better use for making complete iteration through
a collection, it does not have the capability of doing incomplete
iteration because it is not capable of executing the break mechanism, to
cater for break mechanism in loops it best to use scripting components.
- JSON String Processing - To quote MuleSoft documentation "There is no standard language currently for querying JSON data graphs in the same way XPATH can query XML documents. Mule provides a simple query syntax for working with JSON data in Java, called JsonPath", depending on the size of the JSON payload, if it the size is inconsequential then it would be easier for programmatic manipulation if we convert the JSON payload into Java Object. (as shown in Figure 3.0 (4.1))
Thanks for sharing valuable information and very well explained. Keep posting.
ReplyDeletemulesoft training courses
best mulesoft online training