# DataWeave General Guide

The first lines of DataWeave code define the language being used, the dependencies being imported and the output data type. e.g.

```
%dw 2.0    
import withMaxSize from dw::core::Strings   
import leftPad from dw::core::Strings   
output application/json skipNullOn = "everywhere"   
```  

DataWeave code can also contain references to flows, where the output of a flow is to be used as a variable:

```
import main as getExternalData from dwl::otherFlow
```  

Functions in DataWeave aren't really functions, in the conventional sense, but rather they're transformations of variables. They also don't return variables, in the way we'd expect.   

An example would be a function that renames an input variable:
```
fun setGroupId(groupCode) = (   
      groupCode match {
            case "01" -> "First"
            case "02" -> "Second"
            case "03" -> "Third"
            else -> "No Group"
      }
)
```  

Data objects can also be simply declared as variables. e.g.   

```
var oldestRecord = getRecords(
	{
		inputRecords: vars.apiRecord.ExternalAPI.records.*record.@,
		inputOrder: 0
	}
)
```  

Finally, below the transform code, we'd want to define the JSON array fields that are returned to the Mulesoft flow. This is where the functions and variables are used.

```
{
      "timestamp": payload.timestamp,
      "personId": payload.personId,
      "personGroupCode": setGroupId(latestRecord.groupCode),
      "title": payload.title default "No Title",
      "firstName": payload.firstName,
      "surname": payload.surname
}
```  

Sometimes it's a good idea to set default values for fields that might otherwise be null.
 
----  

## Mapping Objects from a Payload  
The following DataWeave code is for a transform in a flow where the payload from an HTTP Request component was declared as a variable called '*extractedRecords*'. Essentially the transform is still mapping data in the response payload to object names.

```
%dw 2.0  
import first from dw::core::Strings  
output application/json  
---
vars.extractedRecords map ((person) ->
{
	"usercode": person.records[0].usercode,
	"person_number": person.personId,
	"username": person.records[0].username,
	"first_names": person.records[0].firstName,
	"surname": person.records[0].surname,
	"gender": person.records[0].gender first 1
})
```  

For gender, I've used ```first 1``` to get the first character of the *gender* value in the payload, instead of the word.

----  

## Distinct Records or Objects in an Array

```
(vars.records map (person) ->
{
	"usercode": person.records[0].personId,
	"id_number": person.personId,
	"username": person.records[0].email,
	"first_names": person.records[0].firstName,
	"surname": person.records[0].surname,
	"email": person.records[0].email
}) distinctBy $.email  
```

----  
## Sorting Data

This is for an unusual, but tricky case, where each record in a JSON response has an array of sub-records, and we need to order by priority field, then by the year field.

```dw
%dw 2.0
output application/json
---
do {
var records = vars.records map (person) -> {
	personId: person.personId,
	records: (
		flatten(
			(
				(person.records orderBy -((($.priority default 0)) as Number))
				groupBy (($.priority default 0) as Number)
			)
			pluck ($ orderBy -((($.year default 1900) as Number)))
		))[0 to 0]
	}
	---
	records
}
```

The DataWeave should group all the sub-records for each person by priority, sort the groups in descending order, then pick the most recent one according to the year field.

----
## Reading From a Data File  

Initially had a DataWeave expression that would map data coming from another Mulesoft API to JSON array objects that the destination API is expecting. It was straightforward:   

```
%dw 2.0
output application/json
---  
payload.results map ((person) ->   
{
      [...] Fields and values here  
})   
```  

To this, I needed to add an additional field for 'group', with a value that's determined by another field called 'groupCode'. The user record should be assigned to a 'VIP' group, if it has a groupCode value that matches one of several in a list.   

We could declare the list of groupCode values as an array variable in the Mulesoft flow directly:  

```["ABC001", "ABC002", "ABC003", "ABC004"]```  

To make the application more configurable, it's better to store the array in a file, in src/main/resources. To read this, the expression in the variable component, in the flow, can be changed to:
```readUrl("classpath://codes-data-file.txt", "text/plain")```  

The first transform after the initial HTTP Request looks something like this:  

```
%dw 2.0   
output application/json
---
payload.results map ((person) ->
{
	"person_number": person.personId,
	"username": person.records[0].username,  
	"group_code": person.records[0].groupCode,
	"first_names": person.records[0].firstName,
	"surname": person.records[0].surname,
	"email": person.records[0].emailAddress,
})   

```  

To the application I added another transform to append a group object to the array. The value of this object depends on whether there's a match between the record's groupCode and any of the codes in the data file:  

```
%dw 2.0
output application/json
---
vars.transformedMessage map ((person) ->
person ++
{
	"group":
		if (vars.groupCodes contains person.group_code) "VIP Group"
		else "Standard Users"
})
```  


For a list of groups:
```
%dw 2.0
output application/json
---
vars.transformedMessage map ((person) -> person ++ 
{
    "groups": 
    (
        ["440012"] ++  // Always include this group
        (
            if (vars.groupCodes contains person.group_code) ["VIP Group"]
            else if (person.dept == "IT Dept" and not (vars.groupCodes contains person.group_code)) ["VIP Group"]
            else []
        )
    ) joinBy ","
})
```

#Mulesoft #DataWeave