Dataweave - how to accumulate json response from http request connector in for each component (Mule 4) - mule

I consume a HTTP request and I need to save and accumulate the JSON response with out any transformation in a variable, I do that but I dont know why the accumulate it correctly, could you please tell me how can I solve that problem.
Json Response By Iteration:
Iteration 1:
{
"orderId": "11111",
"status": "false",
"receivedAt": "2022-07-28T22:45:12.175Z",
"createdAt": "2022-07-28T22:45:12.175Z",
}
Iteration 2:
{
"orderId": "22222",
"status": "false",
"receivedAt": "2022-07-28T22:45:27.907Z",
"createdAt": "2022-07-28T22:45:27.907Z"
}
Dataweave: (csv Payload is the name of the variable where the values accumulated)
%dw 2.0
output application/json
---
if ( vars.counter == 1)
( payload )
else
( vars.csvPayload ) ++ payload
Variable Result:
{
"orderId": "11111",
"status": "false",
"receivedAt": "2022-07-28T22:45:12.175Z",
"createdAt": "2022-07-28T22:45:12.175Z",
"orderId": "22222",
"status": "false",
"receivedAt": "2022-07-28T22:45:27.907Z",
"createdAt": "2022-07-28T22:45:27.907Z"
}
Variable Expected:
[
{
"orderId": "11111",
"status": "false",
"receivedAt": "2022-07-28T22:45:12.175Z",
"createdAt": "2022-07-28T22:45:12.175Z",
},
{"orderId": "22222",
"status": "false",
"receivedAt": "2022-07-28T22:45:27.907Z",
"createdAt": "2022-07-28T22:45:27.907Z"
}
]
NOTE: I don't know why the JSON responses of each iteration are joined in the same object and not as a different object in an array.

The incorrect output is easy. You are concatenating an object to another object. In that case the operator ++ "extracts all the key-values pairs from each object, then combines them together into one result object." according to the documentation. The parenthesis are totally unneeded.
Setting the output to JSON in each iteration -I'm assuming this is inside a foreach- is inefficient since it requires formatting the output to JSON in each iteration only to parse it again in the following one. I recommend to use application/java -which doesn't require parsing/formatting- inside a loop and after the loop convert the entire output to JSON in one go.
You should use an array to hold the values, so assign an empty array ([]) to the variable before the foreach loop to initialize it. Then the counter is unneeded since you can just add elements to the array:
%dw 2.0
output application/java
---
vars.allOrders ++ payload
Then after the foreach just transform the array to JSON:
%dw 2.0
output application/json
---
vars.allOrders

Your requirement is a very common scenario in integrations.
Assuming you are using HTTP Requester inside a for-each loop and initialized the variable to hold the final Payload (lets say var finalPayload = []) before the loop, you have to keep on updating the same variable (append the new data in the same array variable like below) with the response payload received from HTTP Requester for each iteration.
var finalPayload << payload
Ultimately check the finalPayload outside for-each loop, that will give you your desired result.

Related

Mule 4: Replace recurring node value in original payload

I have a recurring node/array in the JSON payload. I need to use value of the wonumber to call another service and the value from this service response has to be used to replace a code value on original message.
{
"element1": "text1",
"element2": "text2",
"element3": "text3",
"InvoiceItemDTO": [
{
"code": "",
"itemNo": "1",
"wonumber": 123
},
{
"code": "",
"itemNo": "2",
"wonumber": 456
}
]
}
The service response will give value for code field, which has to be copied back on original payload code value.
I have applied for each loop on the recurring node to make the call to service for each block in ItemDTO. But I am concerned about putting service response value field back on the original payload at the same index for which the service call was made.
If service call returns code value as abc1, def2 for above payload, then expected final payload is:
{
"element1": "text1",
"element2": "text2",
"element3": "text3",
"InvoiceItemDTO": [
{
"code": "abc1",
"itemNo": "1",
"wonumber": 123
},
{
"code": "def2",
"itemNo": "2",
"wonumber": 456
}
]
}
Any recommendations on how this can be achieved?
the for-each scope does not change the payload. If you want to retain some information gathered during execution of for-each you will need to store that data in a variable. As per the official doc
Every execution of the For Each scope starts with the variables and values from the previous execution of the block. New variables or modifications to existing variables that take place when processing one element are visible during the processing of another element. These changes to variables continue to be available outside the For Each scope.
For your use case you can do the following (Just a pseudo code)
Collect all unique wonumber in an array and store it in a variable
<set-variable variableName="wonumbers" value="#[payload.InvoiceItemDTO.wonumber distinctBy $]"/>
Loop through this variable in the for-each scope
After collecting the code for the wonumber store that as a map in a variable so that you can access it later.
<foreach collection="#[vars.wonumbers]">
<set-variable variableName="currentWonumber" value="#[payload]" />
<do-your-thing.... />
<set-variable variableName="wonumbersToCodeMapping" value="#[(vars.wonumbersToCodeMapping default {}) ++ (vars.currentWonumber): payload.code //however you are getting the code]"/>
</foreach>
Using the wonumbersToCodeMapping you can update the payload using update operator
%dw 2.0
output json
---
payload update {
case .InvoiceItemDTO ->
$ map ((item) -> item update {
case .code -> vars.wonumbersToCodeMapping[item.wonumber as String]
})
}

Array of objects to a single object containing all said objects Dataweave

I am trying to transform the below Array of Objects input:
[
{
"Id": "3",
"Code": "4190484",
"Expense": "Huge Expense "
},
{
"Id": "4",
"Code": "271",
"Expense": "Big Expense"
},
{
"Id": "3",
"Code": "433",
"Expense": "No Expense"
}
]
to this Output of a single object:
{
"Id": "3",
"Code": "4190484",
"Expense": "Huge Expense ",
"Id": "4",
"Code": "271",
"Expense": "Big Expense",
"Id": "3",
"Code": "433",
"Expense": "No Expense"
}
How would you accomplish this in Dataweave?
You can also use the dynamic elements feature of the language:
%dw 2.0
output application/json
---
{(payload)}
Like #aled explained in his answer, you should not be using duplicate keys in JSON.
You can use the reduce() function but be warned that using duplicate keys in JSON is implementation dependent. I think it is a bad design to use duplicate keys in JSON. It might lead to unexpected behaviors. Some implementations might ignore the duplicates. For example DataWeave will return only one Id of the resulting object with payload.Id.
If even after what I mentioned you still want to go ahead it this is an example:
%dw 2.0
output application/json
---
// I don't recommend to use duplicate keys
payload reduce ((item, acc = {}) -> acc ++ item)

Problems matching a long value in Rest Assured json body

I have the following response:
[
{
"id": 53,
"fileUri": "abc",
"filename": "abc.jpg",
"fileSizeBytes": 578466,
"createdDate": "2018-10-15",
"updatedDate": "2018-10-15"
},
{
"id": 54,
"fileUri": "xyz",
"filename": "xyz.pdf",
"fileSizeBytes": 88170994,
"createdDate": "2018-10-15",
"updatedDate": "2018-10-15"
}
]
and I am trying to match the id value to the object in JUnit like so:
RestAssured.given() //
.expect() //
.statusCode(HttpStatus.SC_OK) //
.when() //
.get(String.format("%s/%s/file", URL_BASE, id)) //
.then() //
.log().all() //
.body("", hasSize(2)) //
.body("id", hasItems(file1.getId(), file2.getId()));
But when the match occurs it tries to match an int to a long. Instead I get this output:
java.lang.AssertionError: 1 expectation failed.
JSON path id doesn't match.
Expected: (a collection containing <53L> and a collection containing <54L>)
Actual: [53, 54]
How does one tell Rest Assured that the value is indeed a long even though it might be short enough to fit in an int? I can cast the file's id to an int and it works, but that seems sloppy.
The problem is that when converting from json to java type, int type selected,
one solution is to compare int values.
instead of
.body("id", hasItems(file1.getId(), file2.getId()));
use
.body("id", hasItems(new Long(file1.getId()).intValue(), new Long(file2.getId()).intValue()));

Mule:Dataweave Iteration not working

I am trying to take output from Salesforce & transform it to a json. here is my code:
%dw 1.0
%output application/json
payload map {
headerandlines:{ id : $.Id,
agreementLineID : $.LineItems__r.Id,
netPrice : $.LineItems__r.Price__c,
volume : $.Volume__c,
name : $.Name,
StartDate : $.Start_Date__c,
EndDate : $.End_Date__c,
poField : $.PO_Field__c,
ConsoleNumber : $.Console_Number__c,
Term : $.Term__c,
ownerID : $.OwnerId,
Unit : $.Unit__c,
siteNumber : $.Site_Num__c,
customerNumber : $.Customer_Num__c
}
}
input payload looks like this.. it is a collection of objects. Somehow after the transformation only the first object is sent & rest is clobbered.
[
{
"id": "DA0YAAW",
"LineID": [
"jGEAU",
"jBEAU",
"j6EAE"
],
"Price": [
"50000.0",
"12000.0",
"45000.0"
],
"netPrice": null,
"volume": null,
"name": " Test 2.24",
"StartDate": "2017-02-17",
"EndDate": "2018-02-17",
"poField": "123456",
"ConsoleNumber": "8888888",
"PaymentTerm": "thirty (30)",
"ownerID": “abcd”,
"OperatingUnit": " International Company",
"siteNumber": null,
"customerNumber": null
},
{
"id": "a37n0000000DAMAAA4",
"LineID": [
"JunEAE",
"JuiEAE",
"KdMEAU",
"JuYEAU"
],
"Price": [
"5000.0",
"8000.0",
"5000.0",
"5000.0"
],
"netPrice": null,
"volume": null,
"name": " Test 3.6",
"StartDate": "2017-03-06",
"EndDate": "2018-03-16",
"poField": "12345",
"ConsoleNumber": "123456-",
"PaymentTerm": "30 NET",
"ownerID": “dfgh”,
"OperatingUnit": ", inc.",
"siteNumber": null,
"customerNumber": null
},
….
]
When I call this code from the browser (using API testing) I get the complete payload with multiple objects. When I call this from another API I get only one 1 object indicating it is not looping through. I can confirm that the payload has multiple objects . Is there anything I am missing in terms of looping through this code to extract multiple objects? I assume that '$' notation is good enough for iteration.
#insaneyogi, your input is either incorrect or your dataweave is incorrect.
Here in the input you have specified id in the small. but in dataweave, it is mentioned in capital.
I think the problem here is with your Lineitem and Price type elements. They are collection within and element. In your data mapping $. will take care of the outer object. However, i think the mapping like LineItems__r.Price__c is not correct. It should have proper index , probably LineItems__r.Price__c[0]. Please try that and it should work. First change the input with single element for price or line-item and test.
It looks like the agreementLineID and netPrice are arrays and you need to loop through them with a map operator within the bigger outer map to get all the line items. That should work.

How to get unnamed object instead of an array in case of only one result in Dataweave

We have a transformation with Dataweave which processes a list of objects. We get a json response like that:
{"hotels": [{
"name": "Hotel Oslo",
"propertyCode": "12345",
"currency": "NOK"
},
{
"name": "Hotel Stockholm",
"propertyCode": "12346",
"currency": "SEK"
}]}
However, in the case of only 1 response, we want to have the following response:
{"name": "Hotel Stockholm",
"propertyCode": "12346",
"currency": "SEK"}
We are generating the response like this:
{
hotels: payload.rows map ((row , indexOfRow) -> {
name: row.content.companyName.content,
propertyCode: row.content.propertyCode.content,
currency: row.content.currencyCode.content
})}
What should we put as a condition so that we do not get an array in case of 1 result?
Try this:
%dw 1.0
%output application/json
%function makeHotel(row) {
name: row.name,
propertyCode: row.propertyCode,
currency: row.currency
}
---
{
hotels: payload.rows map ((row , indexOfRow) -> makeHotel(row))
} when ((sizeOf payload.rows) != 1)
otherwise makeHotel(payload.rows[0])
It will give you an empty array on empty input, the simple object for one input and the structure with array when you have more than one input row.
(For test purposes, with a slightly differnt input structure, but the general solution should be clear.)