I got to as far as a json result from log analytic query API HTTP action call:
{
"tables": [
{
"name": "PrimaryResult",
"columns": [
{
"name": "TimeGenerated",
"type": "datetime"
},
{
"name": "_queue",
"type": "string"
},
{
"name": "_messages",
"type": "real"
}
],
"rows": [
[
"2022-06-03T03:20:00Z",
"queue1",
8073
],
[
"2022-06-03T03:20:00Z",
"queue2",
570
]
]
}
]
}
I need transform it to this following format, which essentially an adaptivecards.io card
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "**TimeGenerated**" //Columns[0].Name
},
{
"type": "TextBlock",
"text": "2022-06-03T03:20:00Z" //Rows[0][0]
},
{
"type": "TextBlock",
"text": "2022-06-03T03:20:00Z" //Rows[1][0]
}
]
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "**_queue**" //Columns[1].Name
},
{
"type": "TextBlock",
"text": "queue1" //Rows[0][1]
},
{
"type": "TextBlock",
"text": "queue2" //Rows[1][1]
}
]
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "**_messages**"
},
{
"type": "TextBlock",
"text": "8073"
},
{
"type": "TextBlock",
"text": "570"
}
]
}
]
}
How can I do this? I tried with nested for-loop action, however I'm stuck in composing the final variable that can be inserted in the right place.
It would be ideal to hold a transformed variable ahead of time so that I can just include it in the later stage as POST to another webhook.
Edit: Whilst the column is static, the number rows are dynamic.
Create a LogicApp and load in this definition to see how to do it ...
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Initialize_AdaptiveCard_JSON": {
"inputs": {
"variables": [
{
"name": "XML",
"type": "string",
"value": "{\n \"type\": \"ColumnSet\",\n \"columns\": [\n {\n \"type\": \"Column\",\n \"items\": [\n {\n \"type\": \"TextBlock\",\n \"text\": \"**#{variables('Object Variable')['tables'][0]['columns'][0]['Name']}**\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][0][0]}\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][1][0]}\"\n }\n ]\n },\n {\n \"type\": \"Column\",\n \"items\": [\n {\n \"type\": \"TextBlock\",\n \"text\": \"**#{variables('Object Variable')['tables'][0]['columns'][1]['Name']}**\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][0][1]}\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][1][1]}\"\n }\n ]\n },\n {\n \"type\": \"Column\",\n \"items\": [\n {\n \"type\": \"TextBlock\",\n \"text\": \"**#{variables('Object Variable')['tables'][0]['columns'][2]['Name']}**\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][0][2]}\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][1][2]}\"\n }\n ]\n }\n ]\n}"
}
]
},
"runAfter": {
"Initialize_Object": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Object": {
"inputs": {
"variables": [
{
"name": "Object Variable",
"type": "object",
"value": {
"tables": [
{
"columns": [
{
"name": "TimeGenerated",
"type": "datetime"
},
{
"name": "_queue",
"type": "string"
},
{
"name": "_messages",
"type": "real"
}
],
"name": "PrimaryResult",
"rows": [
[
"2022-06-03T03:20:00Z",
"queue1",
8073
],
[
"2022-06-03T03:20:00Z",
"queue2",
570
]
]
}
]
}
}
]
},
"runAfter": {},
"type": "InitializeVariable"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"Recurrence": {
"evaluatedRecurrence": {
"frequency": "Month",
"interval": 12
},
"recurrence": {
"frequency": "Month",
"interval": 12
},
"type": "Recurrence"
}
}
},
"parameters": {}
}
Basically, you just need to navigate to the specific section of the JSON to retrieve what you need for each individual value.
This is an example of what I'm talking about ...
variables('Object Variable')['tables'][0]['columns'][1]['Name']
... that will get you to the name property for the second column in the first table.
My own solution. Essentially it's broken down into two stages: Initialize the column header and then append the rows at 2nd stage.
Pseudo code
var columns = [];
// initialize column header
for (i=0; i<input.tables[0].columns.length; i++) {
columns.add({
"type": "Column",
"items": [{
"type": "TextBlock",
"text": "**input.tables[0].columns[i].name**",
}]
});
}
// populate row
for (j=0; j<input.tables[0].rows.length; j++) {
for (i=0; i<input.tables[0].columns.length; i++) {
var thisColumn = columns[i];
thisColumn.items.add({
"type": "TextBlock",
"text": "input.tables[0].row[j][i]"
});
}
}
Full solution:
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose_Final": {
"inputs": {
"columns": "#variables('columns')",
"type:": "ColumnSet"
},
"runAfter": {
"Until": [
"Succeeded"
]
},
"type": "Compose"
},
"Initialize_Input": {
"inputs": {
"variables": [
{
"name": "input",
"type": "object",
"value": {
"tables": [
{
"columns": [
{
"name": "_queue",
"type": "string"
},
{
"name": "_messages",
"type": "real"
}
],
"name": "PrimaryResult",
"rows": [
[
"queue1",
8073
],
[
"queue2",
570
],
[
"queue3",
666
]
]
}
]
}
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_variable_columnIdx": {
"inputs": {
"variables": [
{
"name": "columnIdx",
"type": "integer",
"value": 0
}
]
},
"runAfter": {
"Initialize_variable_columns": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_columns": {
"inputs": {
"variables": [
{
"name": "columns",
"type": "array",
"value": []
}
]
},
"runAfter": {
"Initialize_Input": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Select_Column_Header": {
"inputs": {
"from": "#variables('input')?['tables'][0]['columns']",
"select": {
"items": [
{
"text": "**#{item()['name']}**",
"type": "TextBlock"
}
],
"type": "Column"
}
},
"runAfter": {
"Initialize_variable_columnIdx": [
"Succeeded"
]
},
"type": "Select"
},
"Until": {
"actions": {
"Append_to_array_variable": {
"inputs": {
"name": "columns",
"value": {
"items": "#outputs('Compose')",
"type": "Column"
}
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "AppendToArrayVariable"
},
"Compose": {
"inputs": "#union(body('Select_Column_Header')[variables('columnIdx')]['items'], body('Select'))",
"runAfter": {
"Select": [
"Succeeded"
]
},
"type": "Compose"
},
"Increment_variable": {
"inputs": {
"name": "columnIdx",
"value": 1
},
"runAfter": {
"Append_to_array_variable": [
"Succeeded"
]
},
"type": "IncrementVariable"
},
"Select": {
"inputs": {
"from": "#variables('input')?['tables'][0]['rows']",
"select": {
"text": "#item()[variables('columnIdx')]",
"type": "TextBlock"
}
},
"runAfter": {},
"type": "Select"
}
},
"expression": "#greaterOrEquals(variables('columnIdx'), length(variables('input')?['tables'][0]['columns']))",
"limit": {
"count": 60,
"timeout": "PT1H"
},
"runAfter": {
"Select_Column_Header": [
"Succeeded"
]
},
"type": "Until"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"Recurrence": {
"evaluatedRecurrence": {
"frequency": "Month",
"interval": 12
},
"recurrence": {
"frequency": "Month",
"interval": 12
},
"type": "Recurrence"
}
}
},
"parameters": {}
}
Related
I have an issue with epoch date conversion in Azure logic app.
I cannot figure out what I'm doing wrong here.
I have divided my logic app in different steps to first get the Timestamp and remove "/Date" and the trailing "/".
And then I use the add to time function where i am getting an unexpected result.
Input:
"EndDate": "/Date(253402214400000)/",
"StartDate": "/Date(946684800000)/"
Expected output
"EndDate" : "2000-01-01T00:00:00",
"StartDate": "9999-12-31T00:00:00"
Current result:
"EndDate": "1942-11-15T20:26:40.0000000Z",
"StartDate": "2026-10-14T16:21:20.0000000Z"
Logic app code
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose": {
"inputs": {
"root": {
"CostAccountID": "AD100",
"EndDate": "9999-12-31T00:00:00Z",
"StartDate": "2000-01-01T00:00:00Z"
}
},
"runAfter": {},
"type": "Compose"
},
"Compose_2": {
"inputs": {
"root": {
"CostAccountID": "#{body('Parse_JSON')?['root']?['CostAccountID']}",
"EndDate1": "#{int(replace(replace(body('Parse_JSON')?['root']?['EndDate'],'/Date(',''),')/',''))}",
"StartDate1": "#{int(replace(replace(body('Parse_JSON')?['root']?['StartDate'],'/Date(',''),')/',''))}"
}
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "Compose"
},
"Compose_3": {
"inputs": {
"root": {
"CostAccountID": "#{body('Parse_JSON')?['root']?['CostAccountID']}",
"EndDate": "#{addToTime('1970-01-01T00:00:00Z', int(body('Parse_JSON_2')?['root']?['EndDate1']), 'Second')}",
"StartDate": "#{addToTime('1970-01-01T00:00:00Z', int(body('Parse_JSON_2')?['root']?['StartDate1']), 'Second')}"
}
},
"runAfter": {
"Parse_JSON_2": [
"Succeeded"
]
},
"type": "Compose"
},
"Parse_JSON": {
"inputs": {
"content": "#outputs('Compose')",
"schema": {
"properties": {
"root": {
"properties": {
"CostAccountID": {
"type": "string"
},
"EndDate": {
"type": "string"
},
"StartDate": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Parse_JSON_2": {
"inputs": {
"content": "#outputs('Compose_2')",
"schema": {
"properties": {
"root": {
"properties": {
"CostAccountID": {
"type": "string"
},
"EndDate1": {
"type": "string"
},
"StartDate1": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}
},
"runAfter": {
"Compose_2": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Response": {
"inputs": {
"body": "#outputs('Compose_2')",
"statusCode": 200
},
"kind": "http",
"runAfter": {
"Compose_3": [
"Succeeded"
]
},
"type": "Response"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"triggers": {
"manual": {
"inputs": {},
"kind": "Http",
"type": "Request"
}
}
},
"kind": "Stateful"
}
After reproducing from my end, we have observed that you have used the value in milliseconds. Try converting it into seconds and follow the same approach. Below is the code I'm using -
{
"root": {
"CostAccountID": "#{body('Parse_JSON')?['root']?['CostAccountID']}",
"EndDate": "#{addSeconds('1970-01-01T00:00:00Z', div(int(body('Parse_JSON_2')?['root']?['EndDate1']),1000))}",
"StartDate": "#{addSeconds('1970-01-01T00:00:00Z', div(int(body('Parse_JSON_2')?['root']?['StartDate1']),1000))}"
}
}
RESULTS:
I have made changes to your code view. Below is the updated one
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose": {
"inputs": {
"root": {
"CostAccountID": "AD100",
"EndDate": "/Date(253402214400000)/",
"StartDate": "/Date(946684800000)/"
}
},
"runAfter": {},
"type": "Compose"
},
"Compose_2": {
"inputs": {
"root": {
"CostAccountID": "#{body('Parse_JSON')?['root']?['CostAccountID']}",
"EndDate1": "#{int(replace(replace(body('Parse_JSON')?['root']?['EndDate'],'/Date(',''),')/',''))}",
"StartDate1": "#{int(replace(replace(body('Parse_JSON')?['root']?['StartDate'],'/Date(',''),')/',''))}"
}
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "Compose"
},
"Compose_3": {
"inputs": {
"root": {
"CostAccountID": "#{body('Parse_JSON')?['root']?['CostAccountID']}",
"EndDate": "#{addSeconds('1970-01-01T00:00:00Z', div(int(body('Parse_JSON_2')?['root']?['EndDate1']),1000))}",
"StartDate": "#{addSeconds('1970-01-01T00:00:00Z', div(int(body('Parse_JSON_2')?['root']?['StartDate1']),1000))}"
}
},
"runAfter": {
"Parse_JSON_2": [
"Succeeded"
]
},
"type": "Compose"
},
"Parse_JSON": {
"inputs": {
"content": "#outputs('Compose')",
"schema": {
"properties": {
"root": {
"properties": {
"CostAccountID": {
"type": "string"
},
"EndDate": {
"type": "string"
},
"StartDate": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Parse_JSON_2": {
"inputs": {
"content": "#outputs('Compose_2')",
"schema": {
"properties": {
"root": {
"properties": {
"CostAccountID": {
"type": "string"
},
"EndDate1": {
"type": "string"
},
"StartDate1": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}
},
"runAfter": {
"Compose_2": [
"Succeeded"
]
},
"type": "ParseJson"
},
"Response": {
"inputs": {
"body": "#outputs('Compose_3')",
"statusCode": 200
},
"kind": "http",
"runAfter": {
"Compose_3": [
"Succeeded"
]
},
"type": "Response"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {},
"kind": "Http",
"type": "Request"
}
}
}
}
I have a logic app that I want to run once a line item appears in a table each day. once that line item appear, i don't want the logic app to run anymore that day. Can't quite think of how to accomplish this.
Apart from using the related triggers you can use Terminate action to stop the flow.
After using Get entities (V2) I'm trying to Parse the result to get the timestamp of that particular entity to check if the table has an added row or not for that day
After parsing below is the condition that I'm using
{
"contains": [
"#formatDateTime(body('Parse_JSON')?[0]?['Timestamp'],'dd-MM-yyyy')",
"#formatDateTime(utcNow(),'dd-MM-yyyy')"]
}
Below is the complete flow of my Logic App
RESULTS:
To reproduce the same in your Logic App you can use the below code view
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Condition": {
"actions": {
"Terminate": {
"inputs": {
"runStatus": "Succeeded"
},
"runAfter": {},
"type": "Terminate"
}
},
"else": {
"actions": {
"Delay": {
"inputs": {
"interval": {
"count": 2,
"unit": "Hour"
}
},
"runAfter": {},
"type": "wait"
},
"HTTP": {
"inputs": {
"method": "POST",
"uri": "<URI>"
},
"runAfter": {
"Delay": [
"Succeeded"
]
},
"type": "Http"
}
}
},
"expression": {
"and": [
{
"contains": [
"#formatDateTime(body('Parse_JSON')?[0]?['Timestamp'],'dd-MM-yyyy')",
"#formatDateTime(utcNow(),'dd-MM-yyyy')"
]
}
]
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "If"
},
"Get_entities_(V2)": {
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['azuretables']['connectionId']"
}
},
"method": "get",
"path": "/v2/storageAccounts/#{encodeURIComponent(encodeURIComponent('AccountNameFromSettings'))}/tables/#{encodeURIComponent('<TableName>')}/entities"
},
"runAfter": {},
"type": "ApiConnection"
},
"Parse_JSON": {
"inputs": {
"content": "#body('Get_entities_(V2)')?['value']",
"schema": {
"items": {
"properties": {
"FirstName": {
"type": "string"
},
"LastName": {
"type": "string"
},
"PartitionKey": {
"type": "string"
},
"RowKey": {
"type": "string"
},
"Timestamp": {
"type": "string"
},
"odata.etag": {
"type": "string"
}
},
"required": [
"odata.etag",
"PartitionKey",
"RowKey",
"Timestamp",
"FirstName",
"LastName"
],
"type": "object"
},
"type": "array"
}
},
"runAfter": {
"Get_entities_(V2)": [
"Succeeded"
]
},
"type": "ParseJson"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"manual": {
"inputs": {
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {
"$connections": {
"value": {
"azuretables": {
"connectionId": "/subscriptions/<SUB_ID>/resourceGroups/<RG>/providers/Microsoft.Web/connections/azuretables",
"connectionName": "azuretables",
"id": "/subscriptions/<SUB_ID>/providers/Microsoft.Web/locations/centralus/managedApis/azuretables"
}
}
}
}
}
Alternatively, you can use Recurrence trigger to trigger it in regular intervals with the same logic.
I'm trying to get "id" value set to a variable by parsing the below output sample. The REST API call will return multiple values as shown below and I'm interested in only getting the "id" value for the particular name that users has provided/set as input in the workflow earlier either by a parameter value or by initializing a variable. How do I do this value extraction in azure logic app?
Any help much appreciated.
[
{
"id": 1,
"name": "xyz-List",
"data": {
"urls": [
"*.test1.com",
"*.test2.com"
],
"type": "exact"
},
"modify_by": "admin#xyz.com",
"modify_time": "2022-06-29T21:05:27.000Z",
"modify_type": "Created",
"pending": 0
},
{
"id": 2,
"name": "abc-List",
"data": {
"urls": [
"www.mytesting.com"
],
"type": "exact"
},
"modify_by": "admin#xyz.com",
"modify_time": "2022-06-29T21:05:27.000Z",
"modify_type": "Created",
"pending": 0
},
{
"id": 3,
"name": "azure-list",
"data": {
"type": "exact",
"urls": [
"www.xyz.com",
"www.azure-test.com"
],
"json_version": 2
},
"modify_by": "admin#xyz.com",
"modify_time": "2022-09-26T01:25:20.000Z",
"modify_type": "Edited",
"pending": 0
}
]
I have reproduced from my end and could able to make it work by parsing your REST API call value. To iterate through the Parsed JSON I have used a for-each loop and extracted the Id using the below expression and set its value to a variable.
#items('For_each')['id']
Below is the complete flow of my logic app
RESULTS:
To reproduce the same in your logic app you can use the below code view that worked for me.
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose": {
"inputs": [
{
"data": {
"type": "exact",
"urls": [
"*.test1.com",
"*.test2.com"
]
},
"id": 1,
"modify_by": "admin#xyz.com",
"modify_time": "2022-06-29T21:05:27.000Z",
"modify_type": "Created",
"name": "xyz-List",
"pending": 0
},
{
"data": {
"type": "exact",
"urls": [
"www.mytesting.com"
]
},
"id": 2,
"modify_by": "admin#xyz.com",
"modify_time": "2022-06-29T21:05:27.000Z",
"modify_type": "Created",
"name": "abc-List",
"pending": 0
},
{
"data": {
"json_version": 2,
"type": "exact",
"urls": [
"www.xyz.com",
"www.azure-test.com"
]
},
"id": 3,
"modify_by": "admin#xyz.com",
"modify_time": "2022-09-26T01:25:20.000Z",
"modify_type": "Edited",
"name": "azure-list",
"pending": 0
}
],
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "Compose"
},
"For_each": {
"actions": {
"Set_variable": {
"inputs": {
"name": "Id",
"value": "#items('For_each')['id']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"foreach": "#body('Parse_JSON')",
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "Foreach"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "Id",
"type": "integer"
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Parse_JSON": {
"inputs": {
"content": "#outputs('Compose')",
"schema": {
"items": {
"properties": {
"data": {
"properties": {
"type": {
"type": "string"
},
"urls": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"id": {
"type": "integer"
},
"modify_by": {
"type": "string"
},
"modify_time": {
"type": "string"
},
"modify_type": {
"type": "string"
},
"name": {
"type": "string"
},
"pending": {
"type": "integer"
}
},
"required": [
"id",
"name",
"data",
"modify_by",
"modify_time",
"modify_type",
"pending"
],
"type": "object"
},
"type": "array"
}
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "ParseJson"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"manual": {
"inputs": {
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}
Load this into your tenant. You can use basic expressions with a condition to get your result ...
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"For_Each_Array_Item": {
"actions": {
"Condition": {
"actions": {
"Set_ID": {
"inputs": {
"name": "ID",
"value": "#item()['id']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"expression": {
"and": [
{
"equals": [
"#item()['name']",
"abc-List"
]
}
]
},
"runAfter": {},
"type": "If"
}
},
"foreach": "#variables('Array Data')",
"runAfter": {
"Initialize_ID": [
"Succeeded"
]
},
"type": "Foreach"
},
"Initialize_Array_Data": {
"inputs": {
"variables": [
{
"name": "Array Data",
"type": "array",
"value": [
{
"data": {
"type": "exact",
"urls": [
"*.test1.com",
"*.test2.com"
]
},
"id": 1,
"modify_by": "admin#xyz.com",
"modify_time": "2022-06-29T21:05:27.000Z",
"modify_type": "Created",
"name": "xyz-List",
"pending": 0
},
{
"data": {
"type": "exact",
"urls": [
"www.mytesting.com"
]
},
"id": 2,
"modify_by": "admin#xyz.com",
"modify_time": "2022-06-29T21:05:27.000Z",
"modify_type": "Created",
"name": "abc-List",
"pending": 0
},
{
"data": {
"json_version": 2,
"type": "exact",
"urls": [
"www.xyz.com",
"www.azure-test.com"
]
},
"id": 3,
"modify_by": "admin#xyz.com",
"modify_time": "2022-09-26T01:25:20.000Z",
"modify_type": "Edited",
"name": "azure-list",
"pending": 0
}
]
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_ID": {
"inputs": {
"variables": [
{
"name": "ID",
"type": "integer"
}
]
},
"runAfter": {
"Initialize_Array_Data": [
"Succeeded"
]
},
"type": "InitializeVariable"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"ParameterTest1": {
"defaultValue": "\"\"",
"type": "String"
}
},
"triggers": {
"manual": {
"inputs": {
"method": "GET",
"schema": {}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}
My example looks for the name abc-List and if it finds it, it sets the ID variable to be the associated ID of that record it found the name against.
I am surprised to see this, but I witness that in some instance the string value aren't converting as expected to float; mostly returns zero.
The following image shows what happens. As you see I have 2 variables namely var_MarginStr of type string and var_MarginValues of type float in my Logic App. I am assigning var_MarginVal as follows:
var_MarginVal = float(string(variables('var_MarginStr')))
In my working where I trigger my logic app after a new blob is inserted into a blob storage account. The content(*.csv) of the blob is passed to an Azure Function which returns a JSON object. The JSON object is then being looped over each item and is sent to my DB via a Stored Proc.
I have checked my Azure Function which converts the content to JSON working perfectly and the response from such is:
{
"fileName": "20200307-PLC-JKB-Margin.csv",
"agentName": "PLC",
"noOfRecords": 5,
"data": [
{
"accountId": "JKB1234LC",
"marginValue": "0.00"
},
{
"accountId": "JKB4321LC",
"marginValue": "1200000.00"
},
{
"accountId": "JKB5678LC",
"marginValue": "6000000.00"
},
{
"accountId": "JKB8765LC",
"marginValue": "4000000.00"
},
{
"accountId": "JKB9123LC",
"marginValue": "0.00"
}
]
}
Here is my entire LogicApp:
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"CSVTOJSON": {
"inputs": {
"body": "#body('Get_blob_content')",
"function": {
"id": "/subscriptions/xxxx/resourceGroups/xxxx-PRD-PORTAL-01/providers/Microsoft.Web/sites/jksbmarginrequest/functions/CSVTOJSON"
},
"method": "POST",
"queries": {
"fileName": "#triggerBody()?['Name']"
}
},
"runAfter": {
"[Initialize]_For_Composed_AccountId": [
"Succeeded"
]
},
"type": "Function"
},
"Condition": {
"actions": {
"Apply_to_DB": {
"actions": {
"Execute_stored_procedure_(V2)": {
"inputs": {
"body": {
"pActualAccountId": "#{items('Apply_to_DB')?['accountId']}#{variables('var_ComposeAccountIdSuffix')}",
"pAgentId": "#variables('var_AgentName')",
"pEffectiveDate": "#{formatDateTime(utcNow(),'yyyy-MM-dd')}",
"pMarginVal": "#variables('var_MarginVal')"
},
"host": {
"connection": {
"name": "#parameters('$connections')['sql']['connectionId']"
}
},
"method": "post",
"path": "/v2/datasets/#{encodeURIComponent(encodeURIComponent('default'))},#{encodeURIComponent(encodeURIComponent('default'))}/procedures/#{encodeURIComponent(encodeURIComponent('[dbo].[usp_MarginAddUpdate]'))}"
},
"runAfter": {
"[Set]_Margin_Value_Float": [
"Succeeded"
]
},
"type": "ApiConnection"
},
"[Set]_Margin_Value_Float": {
"inputs": {
"name": "var_MarginVal",
"value": "#float(string(variables('var_MarginStr')))"
},
"runAfter": {
"[Set]_Margin_Value_String": [
"Succeeded"
]
},
"type": "SetVariable"
},
"[Set]_Margin_Value_String": {
"inputs": {
"name": "var_MarginStr",
"value": "#items('Apply_to_DB')?['marginValue']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"foreach": "#body('Parse_JSON')?['data']",
"runAfter": {
"For_each": [
"Succeeded"
]
},
"type": "Foreach"
},
"Create_HTML_table": {
"inputs": {
"format": "HTML",
"from": "#body('Parse_JSON')?['data']"
},
"runAfter": {
"Apply_to_DB": [
"Succeeded"
]
},
"type": "Table"
},
"Execute_a_SQL_query_(V2)": {
"inputs": {
"body": {
"actualParameters": {
"pAgentId": "#variables('var_AgentName')"
},
"formalParameters": {
"pAgentId": "VARCHAR(10)"
},
"query": "select * from [dbo].[Parser_MarginFileSettings]\nWhere AgentId =#pAgentId"
},
"host": {
"connection": {
"name": "#parameters('$connections')['sql']['connectionId']"
}
},
"method": "post",
"path": "/v2/datasets/#{encodeURIComponent(encodeURIComponent('default'))},#{encodeURIComponent(encodeURIComponent('default'))}/query/sql"
},
"runAfter": {
"[Set]_Agent_Name": [
"Succeeded"
]
},
"type": "ApiConnection"
},
"For_each": {
"actions": {
"[Set]_Composed_Account_ID_with_Suffix": {
"inputs": {
"name": "var_ComposeAccountIdSuffix",
"value": "#items('For_each')?['Suffix_AccountId']"
},
"runAfter": {},
"type": "SetVariable"
}
},
"foreach": "#body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1']",
"runAfter": {
"Execute_a_SQL_query_(V2)": [
"Succeeded"
]
},
"type": "Foreach"
},
"Parse_JSON": {
"inputs": {
"content": "#body('CSVTOJSON')",
"schema": {
"properties": {
"agentName": {
"type": "string"
},
"data": {
"items": {
"properties": {
"accountId": {
"type": "string"
},
"marginValue": {
"type": "string"
}
},
"required": [
"accountId",
"marginValue"
],
"type": "object"
},
"type": "array"
},
"fileName": {
"type": "string"
},
"noOfRecords": {
"type": "integer"
}
},
"type": "object"
}
},
"runAfter": {},
"type": "ParseJson"
},
"[Set]_Agent_Name": {
"inputs": {
"name": "var_AgentName",
"value": "#body('Parse_JSON')?['agentName']"
},
"runAfter": {
"Parse_JSON": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"else": {
"actions": {
"Compose_Fail-over_Data": {
"inputs": {
"File_Name": "#triggerBody()?['Name']",
"Message": "#body('CSVTOJSON')",
"Status_Code": "#outputs('CSVTOJSON')['statusCode']"
},
"runAfter": {},
"type": "Compose"
},
"For_Error_Listing": {
"inputs": {
"format": "HTML",
"from": "#outputs('Compose_Fail-over_Data')"
},
"runAfter": {
"Compose_Fail-over_Data": [
"Succeeded"
]
},
"type": "Table"
},
"Send_an_email_(V2)": {
"inputs": {
"body": {
"Body": "<p><u><strong>An Error Occured While Updating Margin Request File<br>\n<br>\n</strong></u><u><strong>#{outputs('Compose_Fail-over_Data')}</strong></u><u><strong></strong></u><br>\n</p>",
"Subject": "Margin File Output",
"To": "itops#jkstock.keells.com"
},
"host": {
"connection": {
"name": "#parameters('$connections')['office365']['connectionId']"
}
},
"method": "post",
"path": "/v2/Mail"
},
"runAfter": {
"For_Error_Listing": [
"Succeeded"
]
},
"type": "ApiConnection"
}
}
},
"expression": {
"and": [
{
"equals": [
"#outputs('CSVTOJSON')['statusCode']",
200
]
}
]
},
"runAfter": {
"CSVTOJSON": [
"Succeeded"
]
},
"type": "If"
},
"Get_blob_content": {
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['azureblob']['connectionId']"
}
},
"method": "get",
"path": "/datasets/default/files/#{encodeURIComponent(encodeURIComponent(triggerBody()?['Path']))}/content",
"queries": {
"inferContentType": true
}
},
"runAfter": {},
"runtimeConfiguration": {
"staticResult": {
"name": "Get_blob_content0",
"staticResultOptions": "Disabled"
}
},
"type": "ApiConnection"
},
"[Initialize]_Agent_Name": {
"inputs": {
"variables": [
{
"name": "var_AgentName",
"type": "string",
"value": "PLC"
}
]
},
"runAfter": {
"Get_blob_content": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"[Initialize]_For_Composed_AccountId": {
"inputs": {
"variables": [
{
"name": "var_ComposeAccountIdSuffix",
"type": "string"
}
]
},
"runAfter": {
"[Initialize]_For_Margin_Value_FLoat": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"[Initialize]_For_Margin_Value_FLoat": {
"inputs": {
"variables": [
{
"name": "var_MarginVal",
"type": "float",
"value": 0
}
]
},
"runAfter": {
"[Initialize]_Margin_Value_String": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"[Initialize]_Margin_Value_String": {
"inputs": {
"variables": [
{
"name": "var_MarginStr",
"type": "string"
}
]
},
"runAfter": {
"[Initialize]_Agent_Name": [
"Succeeded"
]
},
"type": "InitializeVariable"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"staticResults": {
"Get_blob_content0": {
"outputs": {
"headers": {},
"statusCode": "OK"
},
"status": "Succeeded"
}
},
"triggers": {
"When_a_blob_is_added_or_modified_(properties_only)": {
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['azureblob']['connectionId']"
}
},
"method": "get",
"path": "/datasets/default/triggers/batch/onupdatedfile",
"queries": {
"folderId": "JTJmcGxj",
"maxFileCount": 1
}
},
"metadata": {
"JTJmcGxj": "/plc"
},
"recurrence": {
"frequency": "Second",
"interval": 3
},
"splitOn": "#triggerBody()",
"type": "ApiConnection"
}
}
},
"parameters": {
"$connections": {
"value": {
"azureblob": {
"connectionId": "/subscriptions/xxxx/resourceGroups/xxx-PRD-PORTAL-01/providers/Microsoft.Web/connections/azureblob-2",
"connectionName": "azureblob-2",
"id": "/subscriptions/xxxx/providers/Microsoft.Web/locations/southeastasia/managedApis/azureblob"
},
"office365": {
"connectionId": "/subscriptions/xxxx/resourceGroups/xxx-PRD-PORTAL-01/providers/Microsoft.Web/connections/office365",
"connectionName": "office365",
"id": "/subscriptions/xxxx/providers/Microsoft.Web/locations/southeastasia/managedApis/office365"
},
"sql": {
"connectionId": "/subscriptions/xxxx/resourceGroups/xxx-PRD-PORTAL-01/providers/Microsoft.Web/connections/sql-6",
"connectionName": "sql-6",
"id": "/subscriptions/xxxx/providers/Microsoft.Web/locations/southeastasia/managedApis/sql"
}
}
}
}
}
When a For Each runs by default it runs iterations concurrently. The variables are not guaranteed to use the value for the current iteration. If you change the For Each to run sequentially it will work with the variables. In the For Each settings turn on Concurrency Control and set the Degree of Parallelism to 1. The other option is to remove the use of variables in the loop if you want it to run in parallel.
The Microsoft documentation for loop on this page https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-control-flow-loops says
To get predictable results from operations on variables during each
loop iteration, run those loops sequentially. For example, when a
concurrently running loop ends, the increment, decrement, and append
to variable operations return predictable results. However, during
each iteration in the concurrently running loop, these operations
might return unpredictable results.
I have two array variables in for each i compare values one by one abd process further but issue is it is taking to much time for executing when I have 1000 of records.
Variable_1 having around 10 records
Variable_2 which is output if HTTP will have around 1000+ records
Variable_1 =
[
{
"Name": "AAA",
"OwnerId": "121a",
},
{
"Name": "BBB",
"OwnerId": "123b",
},
...
]
and
Variable_2 =
[
{
"Demo_Name": "AAA"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "BBB"
}
....
]
I have used 2 for each one is forVariable_1and another is forVariable_2``.
and comparing Variable_1.Name == Variable_2.Demo_Name one by one.
so when Variable_2 is having more than 1000+ records it took around a hour to check condition one by one
any solution for this to do it in a fast way?
outer loop is taking around 2h
If I try concurrent execution then increment variable set wrong
values
I tried doing this with Azure Storage Tables as a source for the result, just to see how fast I could iterate through Var2 with 1465 records and Var1 with 9 records. I do it twice here to see just to figure out which way was easiest to do it in. The result here was XXXX matches, the values stored in an Azure Storage Table which I would fetch after the loops were done. I have added a Terminate Action which you could move them before the action For each just to see use one of them for each loop instead of both.
For the record, not sure if this is exactly what you want. Also with this way, if you switch the For each array to loop over Var1 instead of Var2 It was done in less then 10 seconds with all objects. If you make it into different arrays you can filter the two arrays, you can probably use an intersection also. All you need to do is to fetch the records from the Azure Storage Table which is in JSON notation, now with GUIDs as ids.
4 Minutes
This is the flow for the first one of the two loops, the one I like the most, feels cleaner.
Storage Table result which you would need to truncate or something when you are done or something:
You need an Azure Storage Table to offload the results to.
When posting this I reduced Var2 in size as it made no sense in posting 1600 objects.
Code:
{
"$connections": {
"value": {
"azuretables": {
"connectionId": "/subscriptions/*YOUR SUBSCRIPTION ID*/resourceGroups/*RESOURCEGROUP NAME*/providers/Microsoft.Web/connections/azuretables",
"connectionName": "azuretables",
"id": "/subscriptions/*YOUR SUBSCRIPTION ID*/providers/Microsoft.Web/locations/westeurope/managedApis/azuretables"
}
}
},
"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": {
"actions": {
"Insert_Entity": {
"inputs": {
"body": {
"PartitionKey": "#{items('For_each_2')['Name']}",
"RowKey": "#{guid()}"
},
"host": {
"connection": {
"name": "#parameters('$connections')['azuretables']['connectionId']"
}
},
"method": "post",
"path": "/Tables/#{encodeURIComponent('stackoverflowforeachtmp')}/entities"
},
"runAfter": {},
"type": "ApiConnection"
}
},
"expression": {
"and": [
{
"equals": [
"#items('For_each_2')['Name']",
"#items('For_each')['Demo_Name']"
]
}
]
},
"runAfter": {},
"type": "If"
}
},
"foreach": "#variables('TestValueVariable1')",
"runAfter": {
"Set_TestVariable2": [
"Succeeded"
]
},
"runtimeConfiguration": {
"concurrency": {
"repetitions": 50
}
},
"type": "Foreach"
},
"Set_TestVariable1": {
"inputs": {
"name": "TestValueVariable2",
"value": "#{items('For_each')['Demo_Name']}"
},
"runAfter": {},
"type": "SetVariable"
},
"Set_TestVariable2": {
"inputs": {
"name": "TestValueVariable1",
"value": "#variables('Variable 1')"
},
"runAfter": {
"Set_TestVariable1": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"foreach": "#array(variables('Variable 2'))",
"runAfter": {
"For_each_3": [
"Succeeded"
]
},
"runtimeConfiguration": {
"concurrency": {
"repetitions": 50
}
},
"type": "Foreach"
},
"For_each_3": {
"actions": {
"Filter_array": {
"inputs": {
"from": "#array(variables('Variable 1'))",
"where": "#equals(items('For_each_3')['Demo_Name'], item()['Name'])"
},
"runAfter": {},
"type": "Query"
},
"For_each_4": {
"actions": {
"Insert_Entity_2": {
"inputs": {
"body": {
"EvaluationKey": "#{items('For_each_3')['Demo_Name']}-#{items('For_each_4')['Name']}",
"Owner": "#{items('For_each_4')['OwnerId']}",
"PartitionKey": "#{items('For_each_4')['Name']}",
"RowKey": "#{guid()}"
},
"host": {
"connection": {
"name": "#parameters('$connections')['azuretables']['connectionId']"
}
},
"method": "post",
"path": "/Tables/#{encodeURIComponent('stackoverflowforeachtmp')}/entities"
},
"runAfter": {},
"type": "ApiConnection"
}
},
"foreach": "#body('Filter_array')",
"runAfter": {
"Filter_array": [
"Succeeded"
]
},
"type": "Foreach"
}
},
"foreach": "#array(variables('Variable 2'))",
"runAfter": {
"TestValueVariable2": [
"Succeeded"
]
},
"type": "Foreach"
},
"Get_entities": {
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['azuretables']['connectionId']"
}
},
"method": "get",
"path": "/Tables/#{encodeURIComponent('stackoverflowforeachtmp')}/entities"
},
"runAfter": {
"For_each": [
"Succeeded"
]
},
"runtimeConfiguration": {
"paginationPolicy": {
"minimumItemCount": 30000
}
},
"type": "ApiConnection"
},
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "Variable 1",
"type": "Array",
"value": [
{
"Name": "AAA",
"OwnerId": "121a"
},
{
"Name": "BBB",
"OwnerId": "123b"
},
{
"Name": "AAA",
"OwnerId": "123b"
},
{
"Name": "BBB",
"OwnerId": "123b"
},
{
"Name": "FFF",
"OwnerId": "123b"
},
{
"Name": "BBB",
"OwnerId": "123b"
},
{
"Name": "DDD",
"OwnerId": "123b"
},
{
"Name": "CCC",
"OwnerId": "123b"
},
{
"Name": "BBB",
"OwnerId": "123b"
}
]
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_variable_2": {
"inputs": {
"variables": [
{
"name": "Variable 2",
"type": "Array",
"value": [
{
"Demo_Name": "AAA"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "CCC"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "FFF"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "BBB"
},
{
"Demo_Name": "FFF"
},
{
"Demo_Name": "AAA"
}
]
}
]
},
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Set_variable": {
"inputs": {
"name": "TestValueVariable2",
"value": "#{length(body('Get_entities')?['value'])}"
},
"runAfter": {
"Terminate": [
"Succeeded"
]
},
"type": "SetVariable"
},
"Terminate": {
"inputs": {
"runStatus": "Succeeded"
},
"runAfter": {
"Get_entities": [
"Succeeded"
]
},
"type": "Terminate"
},
"TestValueVariable1": {
"inputs": {
"variables": [
{
"name": "TestValueVariable1",
"type": "Array"
}
]
},
"runAfter": {
"Initialize_variable_2": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"TestValueVariable2": {
"inputs": {
"variables": [
{
"name": "TestValueVariable2",
"type": "String"
}
]
},
"runAfter": {
"TestValueVariable1": [
"Succeeded"
]
},
"type": "InitializeVariable"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"Recurrence": {
"recurrence": {
"frequency": "Month",
"interval": 3
},
"type": "Recurrence"
}
}
}
}