Overview of Mule 4.3 New DW Functions

11 Jun, 2020 | 5 minutes read

The 4.3 version of the Mule runtime engine provided some enhancements and changes in the API Gateway, the Core components (the Until Successful component and the Batch Aggregator are improved) and the DataWeave (DataWeave support for literal types was added, an easy way to operate with periods was provided, the option for defining output MIME type separately from the output data format was added, there are updates in several DataWeave Modules (the Intersection type is introduced), new DataWeave reader and writer properties, improvements in parsing the error messages, etc).

In this article, we will describe some useful functions for every senior Integration developer that can help us speed up our everyday work. Our focus will be on some of the new DW functions, part of the Mule 4.3 runtime.

Update function

Scenario 1 – We need to update the position field in all objects from “Technical Consultant” to “Senior Technical Consultant”. How can we accomplish this?

Solution 1 – We can use the map and the mapObject functions, to iterate through the input payload and go through the key/value pairs in each of the objects of the array. If the key name is “position” we will update its value.

dw script:


%dw 2.0
output application/json
---
payload map (employee, indexOfEmployee) ->
employee mapObject (value, key) ->
 (key): if ((key as String) == "position") "Senior Technical Consultant" else value

input payload:


[
            {
                        "id": 1,
                        "name": "Jane",
                        "lastName": "Smith",
                        "email": "jane.smith@iw.com",
                        "position": "Technical Consultant"
            },
            {
                        "id": 2,
                        "name": "Jo",
                        "lastName": "Martin",
                        "email": "jo.martin@iw.com",
                        "position": "Technical Consultant"
            }
]

output:


[
  {
    "id": 1,
    "name": "Jane",
    "lastName": "Smith",
    "email": "jane.smith@iw.com",
    "position": "Senior Technical Consultant"
  },
  {
    "id": 2,
    "name": "Jo",
    "lastName": "Martin",
    "email": "jo.martin@iw.com",
    "position": "Senior Technical Consultant"
  }
 ] 

Solution 2 – Using the update(Array, String): Any function that is a part of the Values module (dw::util::Values). It will update all objects within the specified array with the given string value. Our dw script now looks simplified having the same output:

dw script:


%dw 2.0
output application/json
import * from dw::util::Values
---
payload update "position" with "Senior Technical Consultant"

Scenario 2 – We need to update the street field in the billingAddress object.

Solution 1 – We can use a recursive function in order to locate the correct object, and the second function in order to update the required value from the corresponding key/value pair. Our functions have four arguments to ensure that we are updating the correct field (e – payload, pred1 – lambda expression that checks if the object key is “billingAddress”, fn – lambda expression that updates the value of the key/pair and pred2 is another lambda expression that checks if the key is “street”).

 dw script:


%dw 2.0
output application/json
fun updateStreet (e,pred1,fn,pred2) =
  e match {
    case is Array  -> $ map updateStreet($, pred1, fn, pred1)
    case is Object -> $ mapObject ((v, k) ->
                        if (pred1(k))
                         {(k): updateTheStreet(v,pred1,fn,pred2)}
                        else
                          {(k): updateStreet(v, pred1, fn, pred2)}
                          )
    else -> $
}
fun updateTheStreet (e,pred1,fn,pred2) =
e mapObject ((v,k) ->
       if (pred2(k))
        {(k): fn(v)}
     else
        {(k): updateStreet(v, pred1, fn, pred2)}
)
---
updateStreet(
  payload,
  ((key) -> key ~= "billingAddress"),
  ((v) -> "South St"),
  ((k) -> k ~= "street")

input payload:


{
            "firstName": "John",
            "lastName": "Doe",
            "eMailAddress": "john.doe@iw.com",
            "billingAddress": {
                        "street": "North St",
                        "houseNumber": 5,
                        "postalCode": "40684",
                        "city": "Greensboro",
                        "state": "VT"
            },
            "shippingAddress": {
                        "street": "North St",
                        "houseNumber": 5,
                        "postalCode": "40684",
                        "city": "Greensboro",
                        "state": "VT"
            }
}

output:


{
  "firstName": "John",
  "lastName": "Doe",
  "eMailAddress": "john.doe@iw.com",
  "billingAddress": {
    "street": "South St",
    "houseNumber": 5,
    "postalCode": "40684",
    "city": "Greensboro",
    "state": "VT"
  },
  "shippingAddress": {
    "street": "North St",
    "houseNumber": 5,
    "postalCode": "40684",
    "city": "Greensboro",
    "state": "VT"
  }
}

Solution 2 – Using the update (Array | Object | Null, Array<String | Number | PathElement>): Any function. It updates the value at the specified path with the given value and will produce the same output reducing our code:

dw script:


%dw 2.0
output application/json
import * from dw::util::Values
---
payload update ["billingAddress", field("street")] with "South St"

withMaxSize function

We have a scenario where we should restrict the number of characters in a given string.

Solution 1 – We can create a function (checkLenght) that has 2 input parameters: e – the string that should be checked and l – the max size allowed. It cuts the extra characters (in case of null it will return null).

dw script:


%dw 2.0
output application/json
fun checkLenght(e,l) =
if (e != null and sizeOf(e) > l) e [0 to l-1]
else e
---
{
   filed1: checkLenght("abcde",10),
   filed2: checkLenght("abcde",5),
   filed3: checkLenght("abcde",3),
   filed4: checkLenght("",15),
   filed5: checkLenght(null,20),
}

output:


{
  "filed1": "abcde",
  "filed2": "abcde",
  "filed3": "abc",
  "filed4": "",
  "filed5": null
}

Solution2 -Using the withMaxSize(String, Number): String function (and the helper function withMaxSize(Null, Number):Null). The withMaxSize function trims the characters from left to right in order for the string length to meet the provided limit. It also works with null values – it returns null when we have null value as an input. It is part of the Strings module (dw::core::Strings):

dw script:


%dw 2.0
import withMaxSize from dw::core::Strings
output application/json
---
{
   filed1: "abcde" withMaxSize 10,
   filed2: "abcde" withMaxSize 5,
   filed3: "abcde" withMaxSize 3,
   filed4: "" withMaxSize 15,
   filed5: null withMaxSize 20,
}

The output is the same as the one in Solution 1.

someEntry and everyEntry functions

Scenario – We need to create 2 arrays – one with the objects that contain at least one empty value, and second with the objects having all values filled correctly.

Solution 1 – We can create functions that will use the filter and filterObject functions in order to put the objects in the corresponding arrays. Using similar functions for determining which objects meet the requirement, our code will look as below:

dw script:


%dw 2.0
output application/json
import * from dw::core::Objects
fun getCorrectObjects(e) =
e filter ($ filterObject $ == "") == {}
fun getErrorObjects(e) =
e filter ($ filterObject $ == "") != {}
---
{
       errorObjects: getErrorObjects(payload),
       correctObjects: getCorrectObjects(payload)
}

input payload:


[
  {
    "id": "1",
    "add1": "add1",
    "add2": "add2",
    "add3": "add3",
    "phone": "1234567890"
  },
  {
    "id": "2",
    "add1": "",
    "add2": "add2",
    "add3": "add3",
    "phone": "1234567890"
  },
  {
    "id": "3",
    "add1": "add1",
    "add2": "add2",
    "add3": "",
    "phone": ""
  }
] 

output:


{
  "errorObjects": [
    {
      "id": "2",
      "add1": "",
      "add2": "add2",
      "add3": "add3",
      "phone": "1234567890"
    },
    {
      "id": "3",
      "add1": "add1",
      "add2": "add2",
      "add3": "",
      "phone": ""
    }
  ],
  "correctObjects": [
    {
      "id": "1",
      "add1": "add1",
      "add2": "add2",
      "add3": "add3",
      "phone": "1234567890"
    }
  ]
} 

Solution 2 – As you already know, integration with Mule is more than valuable, so let’s move on. Another way to achieve this is to filter the payload and use the someEntry(Object, (value: Any, key: Key) -> Boolean): Boolean (if at least one entry in the object matches the specified condition, it returns true) and everyEntry(Object, (value: Any, key: Key) -> Boolean): Boolean (if every entry in the object matches the condition, it returns true) functions. They are part of the Objects module (dw::core::Objects). Looking from a performance perspective, this might be a better solution, because someEntry stops iterating after the first negative evaluation of an element in the object. Similarly, everyEntry stops iterating after finding the first element that matches the condition.

dw script:


%dw 2.0
output application/json
import * from dw::core::Objects
---
{
       errorObjects: payload filter ($ someEntry (value, key) -> value == ""),
       correctObjects: payload filter ($ everyEntry (value, key) -> value != "")
}

Conclusion

In this post, we have explained several scenarios where we can use the update, withMaxSize, someEntry and everyEntry functions. As we can see from the above examples, we can exclude the use of recursions, custom functions, and additional logic for some of our requirements. By using some of the new functions in dw, we can write our code cleaner, easier and faster.