Power Automate: Loop Through the Dataverse Child Records and Analyse input/output

Let’s see how we can loop through the child records returned from the Dataverse, and analyse the input / outputs at each step.

Following is the example scenario we will cover.

On deactivation of Account record we want to loop through the related opportunities.

Following are the steps we will cover:

Create Cloud Flow on Account Deactivate

Create a automated cloud flow.

From outside solution.

or from inside solution.

Select Microsoft Dataverse connector and choose trigger

“When a row is added, modified or deleted”

Because this flow will trigger on deactivate, which is update of account.

Update Trigger properties

Set the flow properties as following:

First of all rename the step to Account or anything else meaningful, to easily identify this step.

Then we need to fill step properties

  • Change type: Modified
    • This defines the on change type on which this flow will trigger.
    • Other options are as following
  • Table name: Accounts
    • This option defines on which table we want flow to run.
    • Any table can be selected
  • Scope: Organization
    • This defines the scope of flow, for which users this flow will trigger.
    • Select organization if this flow should trigger for all users.
  • Select columns: statecode
    • Specify comma separated list of columns.
    • Flow will trigger If any of them are modified.
  • Filter rows: statecode eq 1
    • We are specifying flow to run when statecode is 1, which is inactive status for account.
    • In this you specify OData style filter to determine eligible rows.
  • Run as: Modifying user
    • Specify under which user context flow will run.

So far, it should look like following.

Retrieve child opportunities

Next add the Dataverse action to list rows as following.

Rename the step to List opportunities, to identify the step.

Update the List Opportunities step properties as following:

  • Table name: opportunities
    • Specifies which table records we want to retrieve, opportunity in this case.
  • Select columns: name
    • Specify the columns we want to retrieve, it’s good idea to retrieve only the required columns. In this case we are retrieving name column only.
  • Filter rows: _parentaccountid_value eq [Account from Dynamics content]
    • Specify OData style filter to filter rows.
    • Here we have specified to retrieve only the opportunity rows with parent account id matching to triggering account record id.

We can specify more properties as needed, but for this example leaving as it is.

Analyse value, body and body/value – item from list rows step

List rows step returns following Dynamics content.

  • value: value returns the array of records from the specified table in json format.
  • body: body will return the same array of records along with some other properties in body.
  • body/value – item: this contains a single instance of the array item which is single opportunity record in this case.
    • notice when we add this, automatically Apply to each step will be added.

Let’s analyse each of them.

Add three compose steps for each of them

Notice, when we add body/value – item, automatically Apply to each step will be added, and compose step will be nested inside with current item dynamic content.

Add one more step inside apply to each to extract the opportunity id

Set the expression as following to get the opportunityid from opportunity.

This should appear like this, so far.

Update each opportunity record

Next, for example we want to update current opportunity record, current item in the loop. We can do as following.

Add a Dataverse – Update a row step inside Apply to each.

and set the Table name and Row ID properties as following.

Output is from opportunity id compose item in Dynamics content window.

Here we are specifying:

  • Table name: opportunity
    • We want to update the opportunity record.
  • Row ID: id of the opportunity record we want to update, current item id in this case.

We can update any other property as we want to update on this record and same will be updated in Dataverse.

It should appear like this by now,

Save this flow

Analyse flow input / output

To test the flow and analyse the input / output, deactivate an Account record having few opportunities.

Account step

on clicking Show raw inputs

We can see the parameters passed to Dataverse.

On clicking on Show raw outputs.

List of opportunities step

On clicking Show raw inputs

We can see the filter is applied while retrieving the list of opportunities.

On click to download, json. it we can analyse all the returned data from Dataverse.

{
    "statusCode": 200,
    "headers": {
        "Vary": "Accept-Encoding",
        "x-ms-service-request-id": "e20b3c32-6708-414a-8e2a-533514cdf8e4,78409faa-75a8-4dbe-a535-b289b3327575",
        "Cache-Control": "no-cache",
        "Set-Cookie": "ARRAffinity=a50f3777a483fd4e96aadf1d0e2923f38abbc01c3df5d6cb731bbcf61f629039; domain=org1f7aa4d6.crm11.dynamics.com; path=/; secure; HttpOnly,ReqClientId=68e2c649-02b0-4fcc-89e8-d274f8d5c7bc; expires=Wed, 12-Aug-2071 20:39:52 GMT; path=/; secure; HttpOnly,ARRAffinity=a50f3777a483fd4e96aadf1d0e2923f38abbc01c3df5d6cb731bbcf61f629039; domain=org1f7aa4d6.crm11.dynamics.com; path=/; secure; HttpOnly",
        "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
        "REQ_ID": "78409faa-75a8-4dbe-a535-b289b3327575",
        "AuthActivityId": "1c30924d-64e5-45eb-b690-a28fbdf484af",
        "x-ms-dop-hint": "4",
        "x-ms-ratelimit-time-remaining-xrm-requests": "1,198.57",
        "x-ms-ratelimit-burst-remaining-xrm-requests": "7995",
        "OData-Version": "4.0",
        "Preference-Applied": "odata.include-annotations=\"*\"",
        "X-Source": "1113514625493361791435986622061331061211582156719719922214047249116202212155240124105180,1113514625493361791435986622061331061211582156719719922214047249116202212155240124105180",
        "Public": "OPTIONS,GET,HEAD,POST",
        "Timing-Allow-Origin": "*",
        "Date": "Thu, 12 Aug 2021 20:39:53 GMT",
        "Allow": "OPTIONS,GET,HEAD,POST",
        "Content-Type": "application/json; odata.metadata=full",
        "Expires": "-1",
        "Content-Length": "1702"
    },
    "body": {
        "@odata.context": "https://org1f7aa4d6.crm11.dynamics.com/api/data/v9.1/$metadata#opportunities(name)",
        "#Microsoft.Dynamics.CRM.DeleteMultiple": {
            "title": "DeleteMultiple",
            "target": "https://org1f7aa4d6.crm11.dynamics.com/api/data/v9.1/opportunities/Microsoft.Dynamics.CRM.crmbaseentity/Microsoft.Dynamics.CRM.DeleteMultiple"
        },
        "@Microsoft.Dynamics.CRM.totalrecordcount": -1,
        "@Microsoft.Dynamics.CRM.totalrecordcountlimitexceeded": false,
        "value": [
            {
                "@odata.type": "#Microsoft.Dynamics.CRM.opportunity",
                "@odata.id": "https://org1f7aa4d6.crm11.dynamics.com/api/data/v9.1/opportunities(b052fc98-e8f0-ea11-a815-000d3a1b14a2)",
                "@odata.etag": "W/\"1774838\"",
                "@odata.editLink": "opportunities(b052fc98-e8f0-ea11-a815-000d3a1b14a2)",
                "name": "18 Airpot Coffee Makers for Northwind Traders",
                "opportunityid@odata.type": "#Guid",
                "opportunityid": "b052fc98-e8f0-ea11-a815-000d3a1b14a2"
            },
            {
                "@odata.type": "#Microsoft.Dynamics.CRM.opportunity",
                "@odata.id": "https://org1f7aa4d6.crm11.dynamics.com/api/data/v9.1/opportunities(2ec06197-31ec-ea11-a817-000d3a1b14a2)",
                "@odata.etag": "W/\"1773955\"",
                "@odata.editLink": "opportunities(2ec06197-31ec-ea11-a817-000d3a1b14a2)",
                "name": "2 Café Corto for Northwind Traders",
                "opportunityid@odata.type": "#Guid",
                "opportunityid": "2ec06197-31ec-ea11-a817-000d3a1b14a2"
            },
            {
                "@odata.type": "#Microsoft.Dynamics.CRM.opportunity",
                "@odata.id": "https://org1f7aa4d6.crm11.dynamics.com/api/data/v9.1/opportunities(3cbbd39d-d3f0-ea11-a815-000d3a33f3c3)",
                "@odata.etag": "W/\"1774047\"",
                "@odata.editLink": "opportunities(3cbbd39d-d3f0-ea11-a815-000d3a33f3c3)",
                "name": "5 Café BG-1 Pro Grinders for Northwind Traders",
                "opportunityid@odata.type": "#Guid",
                "opportunityid": "3cbbd39d-d3f0-ea11-a815-000d3a33f3c3"
            }
        ]
    }
}

Compose step – Value

Show raw inputs

Value Dynamic content contains all the returned rows.

Show raw outputs

Compose Step – Body

Show raw inputs

Body Dynamic content contains all the returned rows along with some other body properties.

Show raw outputs

Apply to each step

Show raw inputs

We can see a single opportunity record inside apply to each.

Apply to each – opportunityid

Apply to each – Update a row

Show raw inputs

We can see what parameters were passed as update request to Dataverse.

Show raw outputs

Conclusion

In this post we learned how we can loop through the list of child records. And on each step we analysed what are input/output to each step visualize what is going in and out.

Publish Ionic Web App to Firebase Hosting

Firebase hosting allows to host web apps, let’s see how can publish the Ionic app to Firebase hosting.

Following are the steps:

Generate production www web app folder from the Ionic app

Use following command in existing ionic project to generate production web app www folder.

ionic capacitor build browser --prod

This will generate a www folder, which we need to publish to the Firebase hosting.

Prepare Firebase Hosting project to publish the web app

Install Firebase CLI

Install Firebase CLI globally, if not already installed.

npm install -g firebase-tools

Login to Firebase, through Firebase CLI

Login to Firebase through firebase CLI, using following command. This will open a login screen in browser window. Login there.

firebase login

Create Firebase hosting project

Create new directory for firebase hosting project

mkdir hosting

Switch to directory

cd hosting

Initialize Firebase hosting project

Enter following to initialize firebase hosting project.

firebase init

Choose Yes, to initialize the Firebase project

Choose following hosting feature from listed options:

Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys

Next selected Use an existing project, because in my case I had an existing project.

Then select your project from the list.

Next question is

What do you want to use as your public directory? (public)

type www

Next Question

Configure as a single-page app (rewrite all urls to /index.html)? (y/N)

Choose Y

Next question

Set up automatic builds and deploys with GitHub? No

Enter, and firebase initialization will complete.

In the hosting folder one www folder will be created.

Copy www folder to Firebase hosting project

In the hosting folder one www folder will be created.

Replace this folder with the www folder generated above from Ionic production web app build .

Publish the web app

Finally, enter following command to publish the web app.

firebase deploy

Web app will be published to firebase hosting and you will be presented with the published web app url.

Congrats! We successfully published web app from ionic project to the Firebase hosting.

Conclusion

In this post we learned how to publish an Ionic web project to Firebase hosting.

First we generated the www folder from the Ionic app. Then We created and prepared a new project for publishing to Firebase hosting. In the Firebase hosting project we copied the www folder from the Ionic app and then finally published to Firebase hosting using firebase deploy

TypeScript / JavaScript: Create Array Chunks from Given Array

Often we need to process the array in batches rather than processing whole of the array at once. For that purpose we may need to split the array in chunks of a specified size.

Let’s see how we can split the array in chunks of a specified chunk size.

Consider the following array.

const given = [3, 4, 5, 2, 6, 3, 2, 25, 5, 2, 45, 7, 4, 3];

and we want to split this array into chunks of chunk_size = 3

const given = [3, 4, 5, 2, 6, 3, 2, 25, 5, 2, 45, 7, 4, 3];
const chunk_size = 3;

const chunks = Array.from({ length: Math.ceil(given.length / chunk_size) }).map(() => given.splice(0, chunk_size));

console.table(chunks);

Output

Array.from()

Array.from() method creates array of length defined by the length property.

const arr = Array.from({length:3});
console.table(arr);

Output

Array.prototype.map()

map method can be used to transform each element of the array.

It returns a new array of elements applied the specified function.

const array1 = [1, 4, 9, 16];

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(map1);
// expected output: Array [2, 8, 18, 32]

Array.prototype.splice()

splice(start, deleteCount)

splice method can remove the number of elements from the start position and returns the deleted elements.

const months = ['Jan', 'March', 'April', 'June'];
const deletedmonths = months.splice(1, 1);

console.log('Remaining months');
console.table(months);
console.log('Deleted months');
console.table(deletedmonths);

Output

References:

Array.from()

Array.prototype.map()

Array.prototype.splice()

Conclusion

In this post we learned how we can create the chunks of array of a specified length from the given array.

TypeScript / JavaScript: Sort Object Array By Property

Let’s see how we can quickly sort TypeScript / JavaScript object array by property.

We will sort the array in following ways:

Consider following products array.

const products: Product[] = [
    { id: 1, name: 'Product 1', price: 100 },
    { id: 2, name: 'Product 2', price: 200 },
    { id: 3, name: 'Product 3', price: 300 },
    { id: 1, name: 'Product 1', price: 100 }
]

Sort the array by property in increasing order

Suppose we want to sort the array by price in increasing order.

We can use array sort method like following.

products.sort((prodA, prodB) => prodA.price > prodB.price ? 1 : -1);

console.table(products);

Output

Sort the array by property in decreasing order

Now let’s sort the products array by price in decreasing order.

products.sort((prodA, prodB) => prodA.price > prodB.price ? -1 : 1);

console.table(products);

Output

Array.prototype.sort()

Sort method sorts the elements of an array and returns the sorted array.

const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// expected output: Array ["Dec", "Feb", "Jan", "March"]

const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// expected output: Array [1, 100000, 21, 30, 4]

References:

Array.prototype.sort()

Conclusion

In this post we learned how we can sort the object array by property in ascending and descending order.

TypeScript / JavaScript: Remove Duplicates from Object Array using filter and findIndex method

Let’s see how we can efficiently remove the duplicates from the object array.

Consider the following products array.

const products: Product[] = [
    { id: 1, name: 'Product 1', price: 100 },
    { id: 2, name: 'Product 2', price: 200 },
    { id: 3, name: 'Product 3', price: 300 },
    { id: 1, name: 'Product 1', price: 100 }
]

This products array contains one duplicate product.

{ id: 1, name: 'Product 1', price: 100 }

We can remove the duplicates from object array using filter and findIndex array methods as following.

const prods = products.filter((value, index, array) => index == array.findIndex(item => item.id == value.id));

console.table(prods);

Output

Array.prototype.filter()

Filter method returns new array which passes the filter criteria.

const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];

const result = words.filter(word => word.length > 6);

console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]

Array.prototype.findIndex()

findIndex returns the index of the first element that satisfies the conditions, else returns -1.

const array1 = [5, 12, 8, 130, 44];

const isLargeNumber = (element) => element > 13;

console.log(array1.findIndex(isLargeNumber));
// expected output: 3

References:

Array.prototype.filter()

Array.prototype.findIndex()

Conclusion

In this post we learned how we can efficiently remove the duplicates from the object array.