LogicApp - How to check if collection is empty - azure-logic-apps

What I would like to do it if there is addOn array, I would like to append the word "xxx" to the end of the name.
Schema applied to message.
{
"properties": {
"appointment": {
"properties": {
"id": {
"type": "integer"
},
"lines": {
"items": {
"properties": {
"addOn": {
"items": {
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"type": "object"
},
"type": "array"
},
"id": {
"type": "integer"
},
"price": {
"type": "integer"
}
},
"required": [
"id",
"price"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
},
"messageId": {
"type": "string"
}
},
"type": "object"
}
Message 1
{
"messageId": "11",
"appointment": {
"id": 22,
"lines": [
{
"id": 33,
"price": 125.0,
"addOn": [
{
"id": 44,
"name": "test"
}
]
}
]
}
}
Message 2
{
"messageId": "11",
"appointment": {
"id": 22,
"lines": [
{
"id": 33,
"price": 125.0
}
]
}
}
Message 1 works fine but whenever I try and use length or Parse Array I get a message that addOn is null.
How can I put a proper Condition express to not get any errors or do nothing when there is no addOn array.
Logic App.
Message1 - Okay
Message2 - Error
Code View.
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"For_each": {
"actions": {
"For_each_2": {
"actions": {
"Condition_3": {
"actions": {},
"expression": {
"and": [
{
"equals": [
"#empty(items('For_each_2'))",
"True"
]
}
]
},
"runAfter": {},
"type": "If"
}
},
"foreach": "#items('For_each')['addOn']",
"runAfter": {},
"type": "Foreach"
}
},
"foreach": "#body('Parse_JSON')?['appointment']?['lines']",
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "Foreach"
},
"Parse_JSON": {
"inputs": {
"content": "#triggerBody()",
"schema": {
"properties": {
"appointment": {
"properties": {
"id": {
"type": "integer"
},
"lines": {
"items": {
"properties": {
"addOn": {
"items": {
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"type": "object"
},
"type": "array"
},
"id": {
"type": "integer"
},
"price": {
"type": "integer"
}
},
"required": [
"id",
"price"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
},
"messageId": {
"type": "string"
}
},
"type": "object"
}
},
"runAfter": {},
"type": "ParseJson"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"schema": {
"properties": {
"appointment": {
"properties": {
"id": {
"type": "integer"
},
"lines": {
"items": {
"properties": {
"addOn": {
"items": {
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"type": "object"
},
"type": "array"
},
"id": {
"type": "integer"
},
"price": {
"type": "integer"
}
},
"required": [
"id",
"price",
"addOn"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
},
"messageId": {
"type": "string"
}
},
"type": "object"
}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}
Steve

I would like to append the word "xxx" to the end of the name.
For appending you can directly add while setting the "name" variable.
Alternatively, you can also use concat function as below.
concat(body('Parse_JSON')?['appointment']?['lines'][0]['addOn'][0]['name'],variables('xxx'))
where both cases would give the result as the following
How can I put a proper Condition express to not get any errors or do nothing when there is no addOn array.
You can do this in many ways where one of the workarounds would be using if the lines contain "addOn" or not.
"contains": ["#body('Parse_JSON')?['appointment']?['lines'][0]","addOn"]
MESSAGE1 RESULTS:
MESSAGE2 RESULTS:
You can reproduce the same in your logic app using the below codeview
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose": {
"inputs": "#variables('Message')",
"runAfter": {
"For_each": [
"Succeeded"
]
},
"type": "Compose"
},
"For_each": {
"actions": {
"Condition": {
"actions": {
"Set_variable": {
"inputs": {
"name": "Message",
"value": {
"appointment": {
"id": "#body('Parse_JSON')?['appointment']?['id']",
"lines": [
{
"addOn": "addOn is NULL",
"id": "#items('For_each')?['id']",
"price": "#items('For_each')?['price']"
}
]
},
"messageId": "#{body('Parse_JSON')?['messageId']}"
}
},
"runAfter": {},
"type": "SetVariable"
}
},
"else": {
"actions": {
"For_each_2": {
"actions": {
"Set_variable_2": {
"inputs": {
"name": "Message",
"value": {
"appointment": {
"id": "#body('Parse_JSON')?['appointment']?['id']",
"lines": [
{
"addOn": [
{
"id": "#items('For_each_2')?['id']",
"name": "#{concat(body('Parse_JSON')?['appointment']?['lines'][0]['addOn'][0]['name'],variables('xxx'))}"
}
],
"id": "#items('For_each')?['id']",
"price": "#items('For_each')?['price']"
}
]
},
"messageId": "#{body('Parse_JSON')?['messageId']}"
}
},
"runAfter": {},
"type": "SetVariable"
}
},
"foreach": "#items('For_each')['addOn']",
"runAfter": {},
"type": "Foreach"
}
}
},
"expression": {
"and": [
{
"not": {
"contains": [
"#body('Parse_JSON')?['appointment']?['lines'][0]",
"addOn"
]
}
}
]
},
"runAfter": {},
"type": "If"
}
},
"foreach": "#body('Parse_JSON')?['appointment']?['lines']",
"runAfter": {
"Initialize_variable_xxx": [
"Succeeded"
]
},
"type": "Foreach"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "Message",
"type": "object"
}
]
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_xxx": {
"inputs": {
"variables": [
{
"name": "xxx",
"type": "string",
"value": "xxx"
}
]
},
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Parse_JSON": {
"inputs": {
"content": "#triggerBody()",
"schema": {
"properties": {
"appointment": {
"properties": {
"id": {
"type": "integer"
},
"lines": {
"items": {
"properties": {
"addOn": {
"items": {
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"type": "object"
},
"type": "array"
},
"id": {
"type": "integer"
},
"price": {
"type": "integer"
}
},
"required": [
"id",
"price"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
},
"messageId": {
"type": "string"
}
},
"type": "object"
}
},
"runAfter": {},
"type": "ParseJson"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"schema": {
"properties": {
"appointment": {
"properties": {
"id": {
"type": "integer"
},
"lines": {
"items": {
"properties": {
"addOn": {
"items": {
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"type": "object"
},
"type": "array"
},
"id": {
"type": "integer"
},
"price": {
"type": "integer"
}
},
"required": [
"id",
"price",
"addOn"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
},
"messageId": {
"type": "string"
}
},
"type": "object"
}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}

Related

How to construct a JSON when [keys] are seperated from [values] inside Logic App

I have been struggling with this for a long time to construct an output where my [key] is separated from my array value, please help to share your expertise.
Below is the response, i have received from Azure Alert and I'm looking forward to create an output (refer Expected Output image).
{
"tables": [
{
"columns": [
{
"name": "task",
"type": "string"
},
{
"name": "environment",
"type": "string"
},
{
"name": "workspace",
"type": "string"
}
],
"name": "PrimaryResult",
"rows": [
[
"job_name_1",
"PRODUCTION",
"WORKSPACE-1",
],
[
"job_name_2",
"TEST",
"WORKSPACE-2",
]
]
}
]
}
Inside Logic App. I have parsed the json and used a lot of for each loop variations but, every single time, i get stuck in traversing the rows as its a 2d array. Even though, i traverse them, i cannot associate them with columns. Any alternative approaches will be appreciated ?
Input
Expected Output
[
{
"task": "job_name_1",
"environment": "PRODUCTION",
"workspace" : "WORKSPACE-1"
},
{
"task": "job_name_2",
"environment": "TEST",
"workspace" : "WORKSPACE-2"
}
]
I'd be lying if I said this was straight forward but you can load the below definition into your tenant and see a working version.
Note: I made the assumption that you only ever have one table in your top level array given that's what you provided in your question.
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"For_Each_Row": {
"actions": {
"Append_To_Transformed_Array": {
"inputs": {
"name": "Transformed Array",
"value": "#variables('Row Object')"
},
"runAfter": {
"For_Each_Column": [
"Succeeded"
]
},
"type": "AppendToArrayVariable"
},
"For_Each_Column": {
"actions": {
"Add_Property_To_Temp_Row_Object": {
"inputs": {
"name": "Temp Row Object",
"value": "#addProperty(variables('Row Object'), variables('Columns')?[variables('Column Index')]['name'], items('For_Each_Column'))"
},
"runAfter": {},
"type": "SetVariable"
},
"Increment_Column_Index": {
"inputs": {
"name": "Column Index",
"value": 1
},
"runAfter": {
"Set_Row_Object_From_Temp_Row_Object": [
"Succeeded"
]
},
"type": "IncrementVariable"
},
"Set_Row_Object_From_Temp_Row_Object": {
"inputs": {
"name": "Row Object",
"value": "#variables('Temp Row Object')"
},
"runAfter": {
"Add_Property_To_Temp_Row_Object": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"foreach": "#item()",
"runAfter": {
"Reset_Column_Index": [
"Succeeded"
]
},
"type": "Foreach"
},
"Reset_Column_Index": {
"inputs": {
"name": "Column Index",
"value": 0
},
"runAfter": {
"Reset_Temp_Row_Object": [
"Succeeded"
]
},
"type": "SetVariable"
},
"Reset_Row_Object": {
"inputs": {
"name": "Row Object",
"value": {}
},
"runAfter": {},
"type": "SetVariable"
},
"Reset_Temp_Row_Object": {
"inputs": {
"name": "Temp Row Object",
"value": {}
},
"runAfter": {
"Reset_Row_Object": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"foreach": "#variables('Rows')",
"runAfter": {
"Initialize_Column_Index": [
"Succeeded"
]
},
"runtimeConfiguration": {
"concurrency": {
"repetitions": 1
}
},
"type": "Foreach"
},
"Initialize_Column_Index": {
"inputs": {
"variables": [
{
"name": "Column Index",
"type": "integer"
}
]
},
"runAfter": {
"Initialize_Temp_Row_Object": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Columns": {
"inputs": {
"variables": [
{
"name": "Columns",
"type": "array",
"value": "#variables('Data')?['tables'][0]['columns']"
}
]
},
"runAfter": {
"Initialize_Data": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Data": {
"inputs": {
"variables": [
{
"name": "Data",
"type": "object",
"value": {
"tables": [
{
"columns": [
{
"name": "task",
"type": "string"
},
{
"name": "environment",
"type": "string"
},
{
"name": "workspace",
"type": "string"
}
],
"name": "PrimaryResult",
"rows": [
[
"job_name_1",
"PRODUCTION",
"WORKSPACE-1"
],
[
"job_name_2",
"TEST",
"WORKSPACE-2"
]
]
}
]
}
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_Row_Object": {
"inputs": {
"variables": [
{
"name": "Row Object",
"type": "object"
}
]
},
"runAfter": {
"Initialize_Transformed_Array": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Rows": {
"inputs": {
"variables": [
{
"name": "Rows",
"type": "array",
"value": "#variables('Data')?['tables'][0]['rows']"
}
]
},
"runAfter": {
"Initialize_Columns": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Temp_Row_Object": {
"inputs": {
"variables": [
{
"name": "Temp Row Object",
"type": "object",
"value": {}
}
]
},
"runAfter": {
"Initialize_Row_Object": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Transformed_Array": {
"inputs": {
"variables": [
{
"name": "Transformed Array",
"type": "array"
}
]
},
"runAfter": {
"Initialize_Rows": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Result": {
"inputs": "#variables('Transformed Array')",
"runAfter": {
"For_Each_Row": [
"Succeeded"
]
},
"type": "Compose"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"method": "GET",
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}
The main challenges I see with having to do this are ...
The lack of ability to self reference. Because of that, I had to use a Row Object in conjunction with a Temp Row Object when adding the property each time.
The amount of nesting is a little hard to follow but it works.
Performance will be a real burden if you have a lot of rows.
One thing to note is the outer For Each action needs to have the concurrency set to 1, if it's not, you'll run into problems.
This is the end result as you were wanting ...

Azure Logic app access json property depending on value

I'm trying to get a list of reviews from an api.
This gives me a JSON with nested properties.
I manage to get almost all the data I need except from the nested array ["value"]["answers"]["value"] because sometimes the array ["answers"] has one item (with "id" being either "goods", "delivery" or "service"), or 2 or all 3 of them.
So I can't use the index to get what I want.
I need to be able to check the "id" and from that append the "value" to my array.
Is there a way to write an expression that checks the "id" and from then on writes the "value" in my array ?
JSON :
...
"surveyData": [
{
"questionRef": "q1",
"type": "CORE_STAR_RATING",
"properties": {
"title": "How do you evaluate ...?"
},
"value": {
"stars": 5,
"notAnswered": false
},
"userDefinedId": "q1"
},
"userDefinedId": "q2-good"
},
{
"questionRef": "q3",
"type": "DIMENSIONS",
"properties": {
"title": "How do you evaluate on those criterias ?"
},
**"value": {
"answers": [
{
"id": "delivery",
"value": 4,
"userDefinedId": "delivery",
"name": "Delivery",
"firstLabel": "Nul",
"lastLabel": "Good"
},
{
"id": "goods",
"value": 5,
"userDefinedId": "goods",
"name": "Goods",
"firstLabel": "Nul",
"lastLabel": "Good"
},
{
"id": "service",
"value": 5,
"userDefinedId": "service",
"name": "Service",
"firstLabel": "Nul",
"lastLabel": "Good"
}
]**
},
"userDefinedId": "q3-group"
}
]
...
My array so far :
"value": {
"Event": "#items('For_each-Service')?['event']?['type']",
"Delivery": "", <= ??
"Goods": "", <= ??
"Service": "#items('For_each-Service')?['surveyData'][2]?['value']?['answers'][2]?['value']", <= doesn't work
"comment": "#items('For_each-Service')?['reply']?['comment']",
"order date": "#items('For_each-Service')?['transaction']?['date']",
"date": "#items('For_each-Service')?['reply']?['createdAt']",
"email": "#items('For_each-Service')?['customer']?['email']",
"id review": "#items('For_each-Service')?['event']?['id']",
"name client": "#items('For_each-Service')?['customer']?['fullName']",
"status": "#items('For_each-Service')?['state']",
"title": "#items('For_each-Service')?['title']"
}
Thank you
There are a couple of ways to skin this cat, some more efficient than others.
Personally, I would take the following approach.
I created a flow where, basically, the first thing it does is initialise a variable that contains your Answers array as the data ...
The next step is to initialize another variable that stores that array as XML ...
This is the expression contained within ...
xml(json(concat('{ "root": { "answer": ', variables('Answers Array'), '}}')))
In the next step, I'm basically doing what you're doing by constructing an object, only this time, the selection for the values are a little more complex, this is the definition of the body ...
{
"Delivery": #{first(xpath(xml(variables('XML')), '//id[contains(text(), "delivery")]/parent::answer/value/text()'))},
"Goods": #{first(xpath(xml(variables('XML')), '//id[contains(text(), "goods")]/parent::answer/value/text()'))},
"Service": #{first(xpath(xml(variables('XML')), '//id[contains(text(), "service")]/parent::answer/value/text()'))}
}
Using the xpath approach for querying the dataset can cut down the amount of steps you need to take to get the desired result.
If an element is missing, it will simply come back as null ...
Below is one of the workarounds to achieve your requirement. Firstly, I have initialised 3 variables for Delivery, Goods and Service Counts.
Then used Parse_JSON to extract the inner values of the JSON provided.
In the next step I'm trying to iterate through Id's using Switch action with body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['id'] expression and set the variables using below expression inside an until loop.
body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['value']
and finally you can add in your array like below
RESULTS:
You can use below code view to reproduce in your logic app
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose": {
"inputs": {
"value": {
"answers": [
{
"firstLabel": "Nul",
"id": "goods",
"lastLabel": "Good",
"name": "Goods",
"userDefinedId": "goods",
"value": 4
},
{
"firstLabel": "Nul",
"id": "service",
"lastLabel": "Good",
"name": "Service",
"userDefinedId": "service",
"value": 5
}
]
}
},
"runAfter": {
"Initialize_variable_Service_Count": [
"Succeeded"
]
},
"type": "Compose"
},
"Compose_2": {
"inputs": " \"value\": {\n \"Event\": \"#items('For_each-Service')?['event']?['type']\",\n \"Delivery\": \"#{variables('Delivery Count')}\", \n \"Goods\": \"#{variables('Goods Count')}\", \n \"Service\": \"#{variables('Service Count')}\"\n }",
"runAfter": {
"Until": [
"Succeeded"
]
},
"type": "Compose"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "i",
"type": "integer",
"value": 0
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_variable_Delivery_count": {
"inputs": {
"variables": [
{
"name": "Delivery Count",
"type": "integer"
}
]
},
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_Goods_Count": {
"inputs": {
"variables": [
{
"name": "Goods Count",
"type": "integer"
}
]
},
"runAfter": {
"Initialize_variable_Delivery_count": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_Service_Count": {
"inputs": {
"variables": [
{
"name": "Service Count",
"type": "integer"
}
]
},
"runAfter": {
"Initialize_variable_Goods_Count": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Parse_JSON": {
"inputs": {
"content": "#outputs('Compose')",
"schema": {
"properties": {
"value": {
"properties": {
"answers": {
"items": {
"properties": {
"firstLabel": {
"type": "string"
},
"id": {
"type": "string"
},
"lastLabel": {
"type": "string"
},
"name": {
"type": "string"
},
"userDefinedId": {
"type": "string"
},
"value": {
"type": "integer"
}
},
"required": [],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
},
"type": "object"
}
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Until": {
"actions": {
"Increment_variable": {
"inputs": {
"name": "i",
"value": 1
},
"runAfter": {
"Switch": [
"Succeeded"
]
},
"type": "IncrementVariable"
},
"Switch": {
"cases": {
"Case_Delivery": {
"actions": {
"Set_variable_Delivery_Count": {
"inputs": {
"name": "Delivery Count",
"value": "#body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['value']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"case": "delivery"
},
"Case_Goods": {
"actions": {
"Set_variable_Goods_Count": {
"inputs": {
"name": "Goods Count",
"value": "#body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['value']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"case": "goods"
},
"Case_Service": {
"actions": {
"Set_variable_Service_Count": {
"inputs": {
"name": "Service Count",
"value": "#body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['value']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"case": "service"
}
},
"default": {
"actions": {}
},
"expression": "#body('Parse_JSON')?['value']?['answers']?[iterationIndexes('Until')]?['id']",
"runAfter": {},
"type": "Switch"
}
},
"expression": "#equals(variables('i'), length(body('Parse_JSON')?['value']?['answers']))",
"limit": {
"count": 10,
"timeout": "PT1H"
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "Until"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}

How to assign JSON field from one data source to another in Logic App

I have two different JSON data coming into my Logic App:
Data 1:
[
{
"EmployeeCode":"123",
"Username":"abc"
},
{
"EmployeeCode":"456",
"Username":"def"
}
]
Data 2:
[
{
"EmployeeCode":"123",
"Team":"IT"
},
{
"EmployeeCode":"456",
"Team":"Finance"
}
]
And I want to generate final output like this:
Final output:
[
{
"EmployeeCode":"123",
"Username":"abc",
"Team":"IT"
},
{
"EmployeeCode":"456",
"Username":"def",
"Team":"Finance"
}
]
Is there a simple way to achieve this in Logic App itself? Without using JavaScript or Azure Function or anything?
After reproducing from our end here is how we could able to achieve your requirement.
First, we have used 2 Parse JSON for each data to extract the items in the JSON.
then used a condition connector to compare the EmployeeCode, and then merged using compose connector.
However to make the whole JSON to be used for future purposes we have initialized an array variable and then appended the successful runs from the condition connector. Here is my logic app.
RESULTS:
Below is the code view of my logic app
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"FinalJSON": {
"inputs": "#variables('FinalJson')",
"runAfter": {
"For_each": [
"Succeeded"
]
},
"type": "Compose"
},
"For_each": {
"actions": {
"For_each_2": {
"actions": {
"Condition": {
"actions": {
"Append_to_array_variable": {
"inputs": {
"name": "FinalJson",
"value": "#outputs('Compose')"
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "AppendToArrayVariable"
},
"Compose": {
"inputs": {
"EmployeeCode": "#{items('For_each')['EmployeeCode']}",
"Team": "#{items('For_each_2')['Team']}",
"Username": "#{items('For_each')['Username']}"
},
"runAfter": {},
"type": "Compose"
}
},
"expression": {
"and": [
{
"equals": [
"#items('For_each')['EmployeeCode']",
"#items('For_each_2')['EmployeeCode']"
]
}
]
},
"runAfter": {},
"type": "If"
}
},
"foreach": "#body('Parse_JSON2')",
"runAfter": {},
"type": "Foreach"
}
},
"foreach": "#body('Parse_JSON1')",
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "Foreach"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "FinalJson",
"type": "array"
}
]
},
"runAfter": {
"Parse_JSON2": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"JSON1": {
"inputs": [
{
"EmployeeCode": "123",
"Username": "abc"
},
{
"EmployeeCode": "456",
"Username": "def"
}
],
"runAfter": {},
"type": "Compose"
},
"JSON2": {
"inputs": [
{
"EmployeeCode": "123",
"Team": "IT"
},
{
"EmployeeCode": "456",
"Team": "Finance"
}
],
"runAfter": {
"JSON1": [
"Succeeded"
]
},
"type": "Compose"
},
"Parse_JSON1": {
"inputs": {
"content": "#outputs('JSON1')",
"schema": {
"items": {
"properties": {
"EmployeeCode": {
"type": "string"
},
"Username": {
"type": "string"
}
},
"required": [
"EmployeeCode",
"Username"
],
"type": "object"
},
"type": "array"
}
},
"runAfter": {
"JSON2": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Parse_JSON2": {
"inputs": {
"content": "#outputs('JSON2')",
"schema": {
"items": {
"properties": {
"EmployeeCode": {
"type": "string"
},
"Team": {
"type": "string"
}
},
"required": [
"EmployeeCode",
"Team"
],
"type": "object"
},
"type": "array"
}
},
"runAfter": {
"Parse_JSON1": [
"Succeeded"
]
},
"type": "ParseJson"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}

'Send an Email' parameter 'attachment content' cannot be null

I'm creating an Azure Logic App to email a CSV file with data. When there is no data, the Send_an_email_(V2) step is failing and I see the following error in its output:
Parameter 'Attachment Content' cannot be null or empty.
The Send_an_email_(V2) action in the Azure Logic App gets part of it's input from the output of a preceding Create_CSV_table action. It uses the body of the Create_CSV_table output as shown below, in order to construct ContentBytes for an email attachment:
"actions": {
"Create_CSV_table": {
"inputs": {
"format": "CSV",
"from": "#body('Parse_JSON')"
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "Table"
},
"Send_an_email_(V2)": {
"inputs": {
"body": {
"Attachments": [
{
"ContentBytes": "#{base64(body('Create_CSV_table'))}",
"Name": "report.csv"
}
],
"Body": "<p></p>",
"Subject": "My Report",
"To": "me#email.com"
},
"host": {
"connection": {
"name": "#parameters('$connections')['office365']['connectionId']"
}
},
"method": "post",
"path": "/v2/Mail"
},
"runAfter": {
"Create_CSV_table": [
"Succeeded"
]
},
"type": "ApiConnection"
}
When there is no data input into the Create_CSV_table step, the Create_CSV_table step is successful, and the raw output for the Create_CSV_table step shows an empty body as follows:
{
"body": ""
}
Raw input for the failing Send_an_email_(V2) step
{
HTTP stuff ...,
"body": {
"Attachments": [
{
"ContentBytes": "",
"Name": "report.csv"
}
],
"Body": "<p></p>",
"Subject": "Report",
"To": "me#mail.com"
}
}
Raw output for the failing Send_an_email_(V2) step
{
HTTP stuff ...,
"body": {
"status": 400,
"message": "Parameter 'Attachment Content' cannot be null or empty.\r\nclientRequestId: 887e3968-c7e9-4c35-b588-f76fd0e51545",
"error": {
"message": "Parameter 'Attachment Content' cannot be null or empty."
},
"source": "office365-ase.azconn-ase.p.azurewebsites.net"
}
}
How do I handle this? Do I need to implement my own null handling for the "ContentBytes" of the Send_an_email_(V2) input? If so, how do I do that? Or is there another way to handle this. I want an empty email to be sent when there is no CSV content.
I figured out that #{base64(body('Create_CSV_table'))} is an Azure Logic Apps expression (denoted by #), containing functions that act on the JSON for the body of the output of Create_CSV_table and that the enclosing {} results in the output of the expression being a string.
After the Last step you can send to storage account using Create blob(V2) Connection with .csv file extension and then send the same blob content using Get blob content(V2) Connection then Send an email.
Here are the screenshots of LogicApp
In outlook:
Here is the workflow
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Create_CSV_table_2": {
"inputs": {
"format": "CSV",
"from": "#variables('Array')"
},
"runAfter": {
"For_each_2": [
"Succeeded"
]
},
"type": "Table"
},
"Create_blob_(V2)": {
"inputs": {
"body": "#body('Create_CSV_table_2')",
"headers": {
"ReadFileMetadataFromServer": true
},
"host": {
"connection": {
"name": "#parameters('$connections')['azureblob']['connectionId']"
}
},
"method": "post",
"path": "/v2/datasets/#{encodeURIComponent(encodeURIComponent('AccountNameFromSettings'))}/files",
"queries": {
"folderPath": "/ch1container2408",
"name": "TestSample1.csv",
"queryParametersSingleEncoded": true
}
},
"runAfter": {
"Create_CSV_table_2": [
"Succeeded"
]
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
},
"type": "ApiConnection"
},
"For_each_2": {
"actions": {
"Append_to_array_variable": {
"inputs": {
"name": "Array",
"value": {
"created_at": "#items('For_each_2')['created_at']",
"funds": "#items('For_each_2')['funds']",
"id": "#items('For_each_2')['id']",
"pair": "#items('For_each_2')['market']",
"price": "#items('For_each_2')['price']",
"side": "#items('For_each_2')['side']",
"volume": "#items('For_each_2')['volume']"
}
},
"runAfter": {},
"type": "AppendToArrayVariable"
}
},
"foreach": "#body('Parse_JSON')",
"runAfter": {
"Initialize_variable_2": [
"Succeeded"
]
},
"type": "Foreach"
},
"Get_blob_content_(V2)": {
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['azureblob']['connectionId']"
}
},
"method": "get",
"path": "/v2/datasets/#{encodeURIComponent(encodeURIComponent('AccountNameFromSettings'))}/files/#{encodeURIComponent(encodeURIComponent('/<Your Container>','/<Your File>.csv'))}/content",
"queries": {
"inferContentType": true
}
},
"runAfter": {
"Create_blob_(V2)": [
"Succeeded"
]
},
"type": "ApiConnection"
},
"HTTP": {
"inputs": {
"method": "GET",
"uri": "https://api.wazirx.com/api/v2/trades?market=btcusdt"
},
"runAfter": {},
"type": "Http"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "Array",
"type": "array"
}
]
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_2": {
"inputs": {
"variables": [
{
"name": "Table",
"type": "array"
}
]
},
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Parse_JSON": {
"inputs": {
"content": "#body('HTTP')",
"schema": {
"items": {
"properties": {
"created_at": {
"type": "string"
},
"funds": {
"type": "string"
},
"id": {
"type": "integer"
},
"market": {
"type": "string"
},
"price": {
"type": "string"
},
"side": {},
"volume": {
"type": "string"
}
},
"required": [
"id",
"market",
"price",
"volume",
"funds",
"created_at",
"side"
],
"type": "object"
},
"type": "array"
}
},
"runAfter": {
"HTTP": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Send_an_email_(V2)": {
"inputs": {
"body": {
"Attachments": [
{
"ContentBytes": "#{base64(body('Get_blob_content_(V2)'))}",
"Name": "#body('Create_blob_(V2)')?['Name']"
}
],
"Body": "<p>TABLE TEST</p>",
"Subject": "Test",
"To": "<To Address>"
},
"host": {
"connection": {
"name": "#parameters('$connections')['office365']['connectionId']"
}
},
"method": "post",
"path": "/v2/Mail"
},
"runAfter": {
"Get_blob_content_(V2)": [
"Succeeded"
]
},
"type": "ApiConnection"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"manual": {
"inputs": {},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {
"$connections": {
"value": {
"azureblob": {
"connectionId": "<Your ConnectionId>",
"connectionName": "azureblob",
"id": "<Id>"
},
"office365": {
"connectionId": "<Your ConnectionId>",
"connectionName": "office365",
"id": "<Your Id>"
}
}
}
}
}

Azure Logic App HTTP Post JSON to SQL Row

I'm building a Logic app that takes Wordpress Woocommerce Data as HTTP-POST JSON and puts it into a MSSQL Database.
Setup-
HTTP Post URL
SQL Connection
Response 200
When I build the SQL Connection under the design view it creates a for each expression on Products and Coupon.
I get
ExpressionEvaluationFailed. The execution of template action 'For_each' failed: the result of the evaluation of 'foreach' expression '#triggerBody()' is of type 'Null'. The result must be a valid array.
when trying to run the code.
The table is setup with the columns correctly and they map on the design view but i'm not sure why I'm getting the ExpressionEvaluationFailed.
Note:Removed subscription id for security.
Any help would be greatly appreciated. I know that it has something to do with the arrays.
JSON SCHEMA for HTTP POST
{
"items": {
"properties": {
"Customer User Email": {
"type": "string"
},
"Customer User ID": {
"type": "string"
},
"billing_address": {
"type": "string"
},
"billing_city": {
"type": "string"
},
"billing_company": {
"type": "string"
},
"billing_country": {
"type": "string"
},
"billing_email": {
"type": "string"
},
"billing_first_name": {
"type": "string"
},
"billing_last_name": {
"type": "string"
},
"billing_phone": {
"type": "string"
},
"billing_postcode": {
"type": "string"
},
"billing_state": {
"type": "string"
},
"cart_discount": {
"type": "string"
},
"coupons": {
"items": {
"properties": {
"code": {
"type": "string"
},
"discount_amount": {
"type": "string"
},
"discount_amount_tax": {
"type": "string"
}
},
"required": [
"code",
"discount_amount",
"discount_amount_tax"
],
"type": "object"
},
"type": "array"
},
"customer_note": {
"type": "string"
},
"order_date": {
"type": "string"
},
"order_number": {
"type": "string"
},
"order_refund": {
"type": "integer"
},
"order_shipping": {
"type": "string"
},
"order_status": {
"type": "string"
},
"order_subtotal": {
"type": "string"
},
"order_total": {
"type": "string"
},
"order_total_tax": {
"type": "integer"
},
"payment_method_title": {
"type": "string"
},
"products": {
"items": {
"properties": {
"item_price": {
"type": "integer"
},
"line_id": {
"type": "integer"
},
"name": {
"type": "string"
},
"qty": {
"type": "string"
},
"sku": {
"type": "string"
}
},
"required": [
"line_id",
"sku",
"name",
"qty",
"item_price"
],
"type": "object"
},
"type": "array"
},
"shipping_address": {
"type": "string"
},
"shipping_city": {
"type": "string"
},
"shipping_country": {
"type": "string"
},
"shipping_first_name": {
"type": "string"
},
"shipping_last_name": {
"type": "string"
},
"shipping_method_title": {
"type": "string"
},
"shipping_postcode": {
"type": "string"
},
"shipping_state": {
"type": "string"
}
},
"required": [
"order_number",
"order_status",
"order_date",
"customer_note",
"Customer User ID",
"Customer User Email",
"billing_first_name",
"billing_last_name",
"billing_company",
"billing_address",
"billing_city",
"billing_state",
"billing_postcode",
"billing_country",
"billing_email",
"billing_phone",
"shipping_first_name",
"shipping_last_name",
"shipping_address",
"shipping_city",
"shipping_state",
"shipping_postcode",
"shipping_country",
"shipping_method_title",
"payment_method_title",
"cart_discount",
"order_subtotal",
"order_shipping",
"order_refund",
"order_total",
"order_total_tax",
"products",
"coupons"
],
"type": "object"
},
"type": "array"
}
Logic App Code View
{
"$connections": {
"value": {
"sql_1": {
"connectionId": "/subscriptions/---/resourceGroups/cogpubwebapps/providers/Microsoft.Web/connections/sql-3",
"connectionName": "sql-3",
"id": "/subscriptions/---/providers/Microsoft.Web/locations/eastus/managedApis/sql"
}
}
},
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"For_each": {
"actions": {
"For_each_2": {
"actions": {
"For_each_3": {
"actions": {
"Insert_row": {
"inputs": {
"body": {
"Customer_User_Email": "#items('For_each')['Customer User Email']",
"Customer_User_ID": "#items('For_each')['Customer User ID']",
"billing_address": "#items('For_each')['billing_address']",
"billing_city": "#items('For_each')['billing_city']",
"billing_company": "#items('For_each')['billing_company']",
"billing_country": "#items('For_each')['billing_country']",
"billing_email": "#items('For_each')['billing_email']",
"billing_first_name": "#items('For_each')['billing_first_name']",
"billing_last_name": "#items('For_each')['billing_last_name']",
"billing_phone": "#items('For_each')['billing_phone']",
"billing_postcode": "#items('For_each')['billing_postcode']",
"billing_state": "#items('For_each')['billing_state']",
"cart_discount": "#items('For_each')['cart_discount']",
"coupons0code": "#items('For_each_3')['code']",
"coupons0discount_amount": "#items('For_each')['cart_discount']",
"coupons0discount_amount_tax": "#items('For_each_3')['discount_amount_tax']",
"customer_note": "#items('For_each')['customer_note']",
"order_date": "#items('For_each')['order_date']",
"order_number": "#items('For_each') ['order_number']",
"order_refund": "#items('For_each') ['order_refund']",
"order_shipping": "#items('For_each') ['order_shipping']",
"order_status": "#items('For_each')['order_status']",
"order_subtotal": "#items('For_each') ['order_subtotal']",
"order_total": "#items('For_each') ['order_total']",
"order_total_tax": "#items('For_each')['order_total_tax']",
"payment_method_title": "#items('For_each')['payment_method_title']",
"products0item_price": "#{items('For_each_2')['item_price']}",
"products0line_id": "#{items('For_each_2')['line_id']}",
"products0name": "#items('For_each_2')['name']",
"products0qty": "#items('For_each') ['qty']",
"products0sku": "#items('For_each_2')['sku']",
"shipping_address": "#items('For_each')['shipping_address']",
"shipping_city": "#items('For_each')['shipping_city']",
"shipping_country": "#items('For_each')['shipping_country']",
"shipping_first_name": "#items('For_each')['shipping_first_name']",
"shipping_last_name": "#items('For_each')['shipping_last_name']",
"shipping_method_title": "#items('For_each')['shipping_method_title']",
"shipping_postcode": "#items('For_each')['shipping_postcode']",
"shipping_state": "#items('For_each')['shipping_state']"
},
"host": {
"connection": {
"name": "#parameters('$connections')['sql_1']['connectionId']"
}
},
"method": "post",
"path": "/datasets/default/tables/#{encodeURIComponent(encodeURIComponent('[dbo].[PWBSWebOrders]'))}/items"
},
"runAfter": {},
"type": "ApiConnection"
}
},
"foreach": "#items('For_each')['coupons']",
"runAfter": {},
"type": "Foreach"
}
},
"foreach": "#items('For_each')['products']",
"runAfter": {},
"type": "Foreach"
}
},
"foreach": "#triggerBody()",
"runAfter": {},
"type": "Foreach"
},
"Response": {
"inputs": {
"statusCode": 200
},
"kind": "Http",
"runAfter": {
"For_each": [
"Succeeded"
]
},
"type": "Response"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"manual": {
"inputs": {
"method": "POST",
"schema": {
"items": {
"properties": {
"Customer User Email": {
"type": "string"
},
"Customer User ID": {
"type": "string"
},
"billing_address": {
"type": "string"
},
"billing_city": {
"type": "string"
},
"billing_company": {
"type": "string"
},
"billing_country": {
"type": "string"
},
"billing_email": {
"type": "string"
},
"billing_first_name": {
"type": "string"
},
"billing_last_name": {
"type": "string"
},
"billing_phone": {
"type": "string"
},
"billing_postcode": {
"type": "string"
},
"billing_state": {
"type": "string"
},
"cart_discount": {
"type": "string"
},
"coupons": {
"items": {
"properties": {
"code": {
"type": "string"
},
"discount_amount": {
"type": "string"
},
"discount_amount_tax": {
"type": "string"
}
},
"required": [
"code",
"discount_amount",
"discount_amount_tax"
],
"type": "object"
},
"type": "array"
},
"customer_note": {
"type": "string"
},
"order_date": {
"type": "string"
},
"order_number": {
"type": "string"
},
"order_refund": {
"type": "integer"
},
"order_shipping": {
"type": "string"
},
"order_status": {
"type": "string"
},
"order_subtotal": {
"type": "string"
},
"order_total": {
"type": "string"
},
"order_total_tax": {
"type": "integer"
},
"payment_method_title": {
"type": "string"
},
"products": {
"items": {
"properties": {
"item_price": {
"type": "integer"
},
"line_id": {
"type": "integer"
},
"name": {
"type": "string"
},
"qty": {
"type": "string"
},
"sku": {
"type": "string"
}
},
"required": [
"line_id",
"sku",
"name",
"qty",
"item_price"
],
"type": "object"
},
"type": "array"
},
"shipping_address": {
"type": "string"
},
"shipping_city": {
"type": "string"
},
"shipping_country": {
"type": "string"
},
"shipping_first_name": {
"type": "string"
},
"shipping_last_name": {
"type": "string"
},
"shipping_method_title": {
"type": "string"
},
"shipping_postcode": {
"type": "string"
},
"shipping_state": {
"type": "string"
}
},
"required": [
"order_number",
"order_status",
"order_date",
"customer_note",
"Customer User ID",
"Customer User Email",
"billing_first_name",
"billing_last_name",
"billing_company",
"billing_address",
"billing_city",
"billing_state",
"billing_postcode",
"billing_country",
"billing_email",
"billing_phone",
"shipping_first_name",
"shipping_last_name",
"shipping_address",
"shipping_city",
"shipping_state",
"shipping_postcode",
"shipping_country",
"shipping_method_title",
"payment_method_title",
"cart_discount",
"order_subtotal",
"order_shipping",
"order_refund",
"order_total",
"order_total_tax",
"products",
"coupons"
],
"type": "object"
},
"type": "array"
}
},
"kind": "Http",
"type": "Request"
}
}
}
}
Data is received from Woocommerce and put into SQL database row.

Resources