Dataweave script works for preview, fails in actual use - mule

Using Studio 6.1.0
Runtime 3.8.2ee
I've written a dataweave script that works fine when I preview the results in the Transform Message window, but when I actually run my application I get an error which suggests a problem with the dataweave script.
This is the error:
Invalid input "[\n \t\t({\n \t\t\teffectiveTerm:
template.effectiveTerms.effectiveFromTerm,\n \t\t\tattributeCode: "VARU"\n \t\t} as :object
{ class: "edu.mit.courseleaf.domain.backfill.OldAttribute" })\n \t\twhen
template.units.variableUnits == "Y",", expected flatten, typeOf, using, unzip, capitalize,
avg, min, lower, upper, max, singularize, not, dasherize, ordinalize, camelize, trim, sizeOf,
value, pluralize, sum or underscore (line 16, column 24)
(java.util.concurrent.ExecutionException). Message payload is of type: HashMap
Flow with dataweave script, input for preview, json for actual execution are included below. A couple of comments:
The part of the dataweave script referenced in the error is for the attributes property. The goal here is to set the property to a list of OldAttribute objects. The contents of the list depend on two separate element in the source data: the presence of a subjectTemplate.Units.variableUnits == "Y" and a subset of values in the attributes element.
I know we could go directly from JSON to POJO. In the full application, we're doing additional processing on the intermediary HashMap so we want to retain the two step conversion.
edu.mit.courseleaf.domain.backfill.OldAttribute and edu.mit.courseleaf.domain.backfill.SourceSubject are POJOs with default constructors and the proper getters and setters for the properties we're using.
Flow with dataweave script:
<flow name="temp-json-to-map-to-pojo-example-for-so">
<json:json-to-object-transformer returnClass="java.util.HashMap" doc:name="JSON to Object"/>
<set-payload value="#[payload]" mimeType="*/*" doc:name="Set Payload"/>
<dw:transform-message doc:name="Transform Message">
<dw:input-payload doc:sample="sample_data/map.dwl"/>
<dw:set-payload><![CDATA[%dw 1.0
%input payload application/java
%output application/java
%function passThroughAttributes(attributeList) attributeList - "RPT" - "SWE" - "FSEM"
---
using (template = payload.subjectTemplate
, attributes = payload.subjectTemplate.attributes
)
{
subjectCode:template.mainSubjectCode,
subjectNumber:template.mainSubjectNumber,
attributes: flatten [
({
effectiveTerm: template.effectiveTerms.effectiveFromTerm,
attributeCode: "VARU"
} as :object { class: "edu.mit.courseleaf.domain.backfill.OldAttribute" })
when template.units.variableUnits == "Y",
passThroughAttributes(attributes) map (
{
attributeCode: $,
effectiveTerm: template.effectiveTerms.effectiveFromTerm
}
) as :object { class: "edu.mit.courseleaf.domain.backfill.OldAttribute" }
]
}as :object{class : "edu.mit.courseleaf.domain.backfill.SourceSubject"
]]></dw:set-payload>
</dw:transform-message>
</flow>
Sample input for preview:
{
subjectTemplate: {
subjectTemplateId: "43C9C99A88B76AFBE053282701126517",
mainSubjectCode: "HAA",
mainSubjectNumber: "T101",
subjectLevel: "U",
effectiveTerms: {
effectiveFromTerm:"2017SP",
effectiveThruTerm: "999999"
},
units: {
variableUnits: "Y",
lectureUnits: 9,
labUnits: 3,
prepUnits: 0,
totalCreditLimitUnits: 0
},
attributes: ["RPT", "XLIS"]
}
}
Screenshot of preview showing attributes property correctly populated
Actual JSON input for flow
{
"subjectTemplate": {
"subjectTemplateId": "43C9C99A88B76AFBE053282701126517",
"mainSubjectCode": "HAA",
"mainSubjectNumber": "T101",
"subjectLevel": "U",
"effectiveTerms": {
"effectiveFromTerm": "2017SP",
"effectiveThruTerm": "999999"
},
"units": {
"variableUnits": "Y",
"lectureUnits": 9,
"labUnits": 3,
"prepUnits": 0,
"totalCreditLimitUnits": 0
},
"attributes": ["RPT", "XLIS"]
}
}

Please try with group by in dataweave for grouping all the same category product in one array then do further clean as per your requirement. You can refer here for the details.

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]
})
}

How to remove or trim special characters/prefixes from payload in Mule 4

I need to trim/remove the special characters from a payload.
example- input-
�~�{
"Employee": "Sara",
"number": {
"id": "d1q2",
"designation": "CYUL"
}
}
output-
{
"Employee": "Sara",
"number": {
"id": "d1q2",
"designation": "CYUL"
}
}
I am trying to remove the before characters of "Employee" before curly braces in the beginning whatever is present should be removed and just a JSON structure should be present starting from curly braces and before whatever comes should be removed tried everything unable to do. Please help me in resolving my issue.
You can't read the payload as JSON because those characters are not valid in JSON and the DataWeave parser will throw an error. If you can read the payload as text/plain then you can use DataWeave to remove the 4 extra characters from the beginning and re read the remaining data as a JSON, and set the output to JSON too.
%dw 2.0
output application/json
---
read(payload[4 to -1], "application/json")
If you already read the payload you could try setting its MIME type before executing the DataWeave Transform: <set-payload value="#[payload]" mimeTpy="text/plain"/>.

Use each element of var to make a http request in Mule

I have a collection of matches saved to a var. Each match has a gameId. I want to use this gameId to make an API call.
I set up a for each scope using matches with a counter var.
<set-variable value="#[payload]" doc:name="Set Matches" doc:id="bb1093b0-3dd9-4515-8f42-dd496f00477b" variableName="matches"/>
<set-variable value="#[[]]" doc:name="Deaths" doc:id="a7341e19-07f8-418d-b39a-5bc415e75fba" variableName="deaths"/>
<foreach doc:name="For each match" doc:id="506fa944-d46f-4684-9c75-73088265ca80" collection="vars.matches" rootMessageVariableName="matches">
<http:request method="GET" doc:name="GetTimeline" doc:id="63708489-4d10-4e79-b5aa-c8fc723e80c6" config-ref="HTTP_Request_configuration" path="/match/v4/timelines/by-match/{matchId}">
<http:uri-params ><![CDATA[#[output application/java
---
{
"matchId" : vars.matches[vars.counter + 1].gameId
}]]]></http:uri-params>
</http:request>
<ee:transform doc:name="MapDeaths" doc:id="a6e22968-2229-4574-8cae-44225013b9cf">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
frames: payload.frames map ( frame , indexOfFrame ) -> {
events: frame.events map ( event , indexOfEvent ) -> vars.deaths + event
}
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</foreach>
Yet matchId from "matchId" : vars.matches[vars.counter + 1].gameId always evalutes to null. What am I missing there?
Edit:
I'm using Mule 4.2 and the json structure of matches looks like this:
[
{
"matches": [
{
"key": "val",
"key": val,
...
},
{
"key": "val",
"key": val,
...
},
...
Set {matchId} as Query string parameter and define request a Target variable, use that in dataweave
The problem seems to be that your payload and the expression you are using doesn't match (pun not intended). The payload is an array that contains objects, with a matches attribute. The expression apparently assumes that the payload contains a matches attribute, which contains a gameId attribute. That is the reason it returns null.
Also the intention is not clear, given that you didn't show were gameId is in your example.
Let's try with a more detailed example.
For the input:
[
{
"matches": [
{
"keyA": "val1",
"keyB": "val2"
},
{
"keyC": "val3",
"keyD": "val4"
}
]
}
]
The expression payload.matches.keyA returns null.
You could do payload[0].matches[0].keyA to obtain "val1".
This assumes there data is from the first element of the array and you want to use only one key from one of the matches. Of course if you need to iterate on the different arrays levels you will need to use map or group, but it is a different use case.

Mule ESB Multiple when conditions in Dataweave

I am using dataweave, due to sales taxes in each state, I need to assign a each customer from a specific state to a pre-defined user.
However when I create this in dataweave I end up with this as my XML:
<Customer>
<Number>
<Number>
<Number>
<Number>WEB002</Number>
</Number>
</Number>
</Number>
I just need one 'number' node underneath the 'customer' node.
Here is my dataweave:
Customer:{
Number: 'WEB001' when payload.order.billing_address.state == 'Indiana' otherwise
Number: 'WEB002' when payload.order.billing_address.state == 'Kentucky' otherwise
Number: 'WEB003' when payload.order.billing_address.state == 'Illinois' otherwise
Number: 'WEB004'
},
Have tried placing my conditions inside of brackets, no joy, have tried to wrap each condition in brackets, no joy.
Is there a better way to do this?
A very easy and simple way to do that as follows:-
<dw:transform-message doc:name="Transform Message">
<dw:input-payload doc:sample="empty.xml" />
<dw:set-payload><![CDATA[%dw 1.0
%output application/xml
---
{
Customer:
Number: "WEB001" when payload.order.billing_address.state == "Indiana"
otherwise (
"WEB002" when payload.order.billing_address.state == "Kentucky"
otherwise (
"WEB003" when payload.order.billing_address.state == "Illinois"
otherwise "WEB004"))
}
]]></dw:set-payload>
</dw:transform-message>
you can put the mapping between state and number in a map/dictionary which would improve the readability like this:
%var numbers = {
Indiana: 'WEB001',
Kentucky: 'WEB002',
Illinois: 'WEB003'
}
this is what your dataweave would look like:
%dw 1.0
%output application/xml
%var defaultNumber = 'WEB004'
%var numbers = {
Indiana: 'WEB001',
Kentucky: 'WEB002',
Illinois: 'WEB003'
}
---
Customer: {
Number: numbers[payload.order.billing_address.state]
default defaultNumber
}
and here is a screenshot containing sample data and the resulting preview:

Mule Transform Set Dynamic Key From Payload

I want to set up a key value dictionary from 2 different fields in my payload. My first thought is to use a transform component and dynamically set the key, but this code does not work:
%dw 1.0
%output application/java
---
{
payload.objectnumber: payload.objectid
}
I get this error:
Multiple markers at this line
- Invalid input "payload.", expected conditionalKeyValuePair,
keyValuePair, enclosedExpr or objectEnd
- no viable alternative at input 'payload'
- no viable alternative at input '.'
Any ideas on how to get it working?
Input:
[{
objectnumber: 75,
objectid: "abcdefgh"
}]
Expected Output:
[{
"abcdefgh": 75
}]
Put a variable inside bracket (value) to define a dynamic key. Therefore, you can try this code:
%dw 1.0
%output application/json
---
payload map {
($.objectid): $.objectnumber
}
Try the below code
<dw:transform-message metadata:id="b968b3ec-ed0a-492d-bf32-bcfe0d20e9d0" doc:name="Transform Message">
<dw:set-payload><![CDATA[%dw 1.0
%output application/java
%var objectId = payload.objectid
---
objectId :payload.objectnumber
]]></dw:set-payload>
</dw:transform-message>
JSON Input in Postman:
[
{
"objectnumber":75,
"objectid":"abcdefgh"
}
]