C# | Parse Json using System.Text.Json (instead of Json.NET)

In this post lets see how to parse a complex Json content without using Json.NET library and by using the native System.Text.Json namespace.

What and why cant I use Json.NET library?

  • Json.NET (also known as Newtonsoft.Json) is a popular third-party JSON library for .NET.
  • Flexible Serialization, Better Performance, LINQ to JSON, JSON Path, and JSON Schema validation are some of the standout features of Json.NET library.
  • I was unable to utilize the Json.NET library solely due to the fact that it is a third-party tool, and we are obligated not to use it.

Scenario:

Coming to our scenario we have following json content and we need to parse and read the inputs and outputs collection from the actions array.

{
  "actions": [
    {
      "name": "Display select file dialog",
      "status": "Failed",
      "inputs": {
        "allowMultiple": {
          "localizedName": "Allow multiple selection",
          "value": "False",
          "variableName": ""
        },
        "title": {
          "localizedName": "Dialog title",
          "value": "Select the excel file to extract table from...",
          "variableName": ""
        }
      },
      "outputs": {
        "selectedFile": {
          "localizedName": "Selected file",
          "value": "",
          "variableName": "SelectedFile"
        },
        "buttonPressed": {
          "localizedName": "Button pressed",
          "value": "Cancel",
          "variableName": "ButtonPressed"
        }
      }
    },
    {
      "name": "If",
      "status": "Succeeded",
      "inputs": {
        "expression": {
          "localizedName": "Condition",
          "value": "False",
          "variableName": ""
        }
      },
      "outputs": {}
    }
  ],
  "childFlowsLogStatus": "Default"
}

C# Code Snippet:

Following is the C# code snippet , which parses the json content and flattens the inputs and outputs collection json using native System.Text.Json namespace.

using System;
using System.Text;
using System.Text.Json;

namespace ParseJson
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var jsonContent = "{\"actions\":[{\"name\":\"Display select file dialog\",\"status\":\"Failed\",\"inputs\":{\"allowMultiple\":{\"localizedName\":\"Allow multiple selection\",\"value\":\"False\",\"variableName\":\"\"},\"title\":{\"localizedName\":\"Dialog title\",\"value\":\"Select the excel file to extract table from...\",\"variableName\":\"\"}},\"outputs\":{\"selectedFile\":{\"localizedName\":\"Selected file\",\"value\":\"\",\"variableName\":\"SelectedFile\"},\"buttonPressed\":{\"localizedName\":\"Button pressed\",\"value\":\"Cancel\",\"variableName\":\"ButtonPressed\"}}},{\"name\":\"If\",\"status\":\"Succeeded\",\"inputs\":{\"expression\":{\"localizedName\":\"Condition\",\"value\":\"False\",\"variableName\":\"\"}},\"outputs\":{}}],\"childFlowsLogStatus\":\"Default\"}";

                string strInputXML = ParseInputandOutput(jsonContent, "inputs");
                string strOutputXML = ParseInputandOutput(jsonContent, "outputs");
                Console.WriteLine($"Input content : {strInputXML}");
                Console.WriteLine($"Output content : {strOutputXML}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error {ex.Message}");
            }
            finally
            {
                Console.ReadLine();
            }
        }

        private static string ParseInputandOutput(string flowContextDetails, string propertyName)
        {
            StringBuilder sbrRawText = new StringBuilder();
            JsonDocument doc = JsonDocument.Parse(flowContextDetails);
            JsonElement actions = doc.RootElement.GetProperty("actions");

            foreach (JsonElement action in actions.EnumerateArray())
            {
                if (action.GetProperty("status").GetString() == "Failed")
                {
                    JsonElement propertyNodes = action.GetProperty(propertyName);
                    sbrRawText.AppendLine(propertyNodes.ToString());
                }
            }

            return sbrRawText.ToString();
        }
    }
}

Run the console app and you will get the flattened json of inputs and outputs collection.

Hope this blog post gives the idea of how to parse json using native System.Text.Json namespace.

Advertisement
Categories: C# Tags:

Request a Power Apps premium license from org admins

In ‘Power Apps’ users can now request a license from their admin – directly in product.

If a user is trying to run an app that requires a license, they will see the option to ‘Request a license’, in addition to the buy a license or start a trial option (if eligible).

Once a request is submitted, a confirmation message will appear at the top of the page saying the request was successfully sent to their admin.

Read this blog post for more details.

🙂

Categories: PowerApps Tags: ,

Power automate | Child flows | ‘NestedWorkflowDoesNotContainResponseAction’ error

While running a Power automate Child Flows scenario, encountered the following exception.


NestedWorkflowDoesNotContainResponseAction
. To wait on nested workflow ‘xxx-xx-xxxx-xxxx’, it must contain a response action.

Flow Error

To explain the cause of the error and the fix, let me explain my Power automate Child Flows scenario.

  • I’ve 2 Dataverse tables Students and Enrollments.
  • Enrollments table has look up column of Students table.
  • In my flow, I receive interested students ‘EmailIds’ for enrollment in the comma separated format. I read the emails and create Enrollments records.
  • To read the email id’s and create Enrollments records, i’ve created 2 flows,
    • Parent – Enrollment Process – Triggers the child flow ‘Child – Complete Enrollment’ by passing comma separated Email Ids.
    • Child – Complete Enrollment – Reads the comma separated Email Ids passed by ‘Parent – Enrollment Process‘ flow and create rows in Enrollments table.

Following is the step by step process, to create my above Power automate Child Flows scenario.

Lets create the ‘Child flow (Child – Complete Enrollment)’ first.

Create Child flow (Child – Complete Enrollment):

As the ‘Child – Complete Enrollment flow’ reads the comma separated email id’s and creates records in Enrollment table, following are actions.

  • Create an Instant Flow (Child flow should be a Instant Flow).
  • Add 2 input parameters to read the EmailId’s passed from Parent flow.
    • InterestedEmails : Comma separated Email of interested Students to enroll.
    • UninterestedEmails : Comma separated Email of uninterested Students to enroll.
  • Use Split function with Apply to each to read the comma delimited Email ids to array.
    • Below I am splitting InterestedEmails parameter using comma(,) delimiter.
  • Now we will have array of interested Student ‘Email Ids’ after applying the Split function.
  • As a next step, we need to fetch the matching Student record by ‘Email Id’ from Dataverse, use the List Rows action.
    • We need the ‘Student’ because to create the ‘Enrollment’ record, we need to set the ‘Student’ lookup.
  • I’ve renamed the List Rows action to ‘GetStudentbyEmail’ and in ‘Filter rows’, I am passing the ‘Email Id’ from Split function.
  • ‘GetStudentbyEmail’ would return the matching ‘Student’ which will help us to create ‘Enrollment’ record.
  • So add a Add a new row action to create ‘Enrollment’ record.
    • Notice, I set the ‘Student’ lookup with ‘StudentId’ returned from previous ‘GetStudentbyEmail’ action.
  • Save the flow.

Now that we have the Child flow (Child – Complete Enrollment), lets create a Parent flow (Parent – Enrollment Process).

Create Parent flow (Parent – Enrollment Process):

Parent flow can be of any type (i.e., Automated/Instant/Scheduled). For this example, I am taking Instant flow.

  • Create an Instant flow with 2 input parameters.
  • Add a ‘Run a Child Flow‘ action and select our ‘Child – Complete Enrollment‘ flow.
  • Pass the required parameters to the ‘Child – Complete Enrollment‘ by reading parameters of ‘Parent – Enrollment Process‘.

Reason and Fix for ‘NestedWorkflowDoesNotContainResponseAction’ error:

  • If you open the ‘Flow Checker’ of the ‘Parent – Enrollment Process‘ you will notice “Update the child flow for action ‘Run_a_Child_Flow’ to end with a response action.” error.
  • The reason for the error is, Child flow must have one of the 2 following actions.
    • Respond to a Power App or flow (under the Power Apps connector).
    •  Response (on the premium HTTP request/response connector).
  • Save the ‘Child – Complete Enrollment‘ flow and ‘NestedWorkflowDoesNotContainResponseAction’ error should be fixed.

Plugin Registration Tool (PRT) | ‘Invalid plug-in registration stage’ error while unregistering Assembly

While unregistering one of my custom plug-in assemblies from the Plugin Registration Tool (PRT), I got the following error:

Invalid plug-in registration stage. Steps can only be modified in stages BeforeMainOperationOutsideTransaction, BeforeMainOperationInsideTransaction, AfterMainOperationInsideTransaction and AfterMainOperationOutsideTransaction.

Reason:

  • Which means, a Dataverse Custom API record already, mapped with the Plug-in CAT.APIs.ExecuteOnBoardCustomerAPI in the background. Refer ‘Plugin Type’ column below.
  • Hence we can’t unregister the CAT.APIs Assembly without un-mapping the CAT.APIs.ExecuteOnBoardCustomerAPI from the Dataverse Custom API record.

Fix:

  • Either un-map (i.e., Clear the ‘Plugin Type’ column) the CAT.APIs.ExecuteOnBoardCustomerAPI from the Dataverse Custom API record as below.
  • Or delete the Dataverse Custom API record.
  • Proceed with unregistering the Plug-in Assembly from Plugin Registration Tool and it should unregister now.

If you are looking for downloading and launching Plugin Registration Tool, please refer article here.

🙂

Categories: CRM

Power Virtual Agent (PVA) | GPT | Boost conversations (preview)

In this article, I am going to give a quick demo on PVA Boost conversations GPT capability.

What is Boost conversations in PVA

  • By utilizing the boosted conversations in Power Virtual Agents, your bot can find and present information from an external source (i.e., Public facing website).
  • What is an External Source?
    • Pick a publicly accessible website, such as your primary company domain or customer support site.
    • Ensure that the site you select is accurate and up to date – the bot willl use it as the basis for its conversations.
    • Ensure the site is indexed or found by Bing
  • This feature runs on Azure OpenAI service. About OpenAI service.

Lets learn how to use Boost conversations capability.

Steps to use Boost conversations in PVA

  • Provide the details and complete the sign up.
  • In the next screen, provide the External Source website URL as the Boost conversations can find and present information from the website.
    • I’ve given ‘rajeevpentyala.com’ as the External Source.
  • Your text bot is ready to take questions with out the need of creating Topics.
  • Ask a question relevant to ‘rajeevpentyala.com’ which is our External Source and you will get answers in response as below.
  • Now, lets change the website url using ‘Change site’ option.
  • I’ve provided amazon.in as the url and clicked ‘Save’.
  • Ask about latest products and you will get response as below.
  • Try suggestions about products.

Publish the bot:
  • During this preview, you won’t be able to publish bots that have Boost conversations enabled.
  • Publish fails with You’ve boosted your conversational coverage, which is a feature that blocks publishing. Change your settings or contact your administrator to publish this bot error.
  • During the Preview of this feature you’ll need to contact Microsoft Support if you want to publish a bot that has Boost conversations enabled.

URL considerations:

There are some requirements on the type and structure of the External Source URL you use:

  • The URL can have up to two levels of depth (or “sub-paths”, indicated by forward slashes (/)). Your URL can have a trailing forward slash, and this won’t be included in the limit of two slashes.
  • Refer this link for more details.

🙂

[Beginners] Power Platform | Tenant vs Environment

Most of the beginners, gets confused between a Tenant and Environment in Power Platform. Lets understand the differences and learn how to manually fetch ‘Tenant Id’ and ‘Environment Id’.

What is Tenant:

  • A Tenant is the overall organization or instance of Power Platform that a user belongs to.
  • It can be thought of as a container for all the environments, users, and data associated with an organization.
  • A tenant has a unique domain name, such as myorganization.crm.dynamics.com.

What is an Environment:

  • Environment is a space to store, manage, and share your organization’s business data, apps, chatbots, and flows.

How an Environment is different than Tenant:

  • A Tenant is the overall organization or instance of Power Platform.
  • An Environment is a separate space within a Tenant.
  • Multiple environments can exist within a single tenant, and each environment can be customized independently of others.

How to manually fetch ‘Tenant ID’ and ‘Environment ID’:

The ‘Tenant ID’ is assigned to a tenant when it is created. It is generated by Microsoft and cannot be changed. To fetch ‘Tenant ID’ and ‘Environment ID’ in ‘Power Platform’

  • Login to Power Apps maker portal.
  • Select the desired ‘Environment’ from ‘Environment’ dropdown.
  • Select the Settings icon and click on ‘Session Details’.
  • From the ‘Power Apps session details’ popup, copy ‘Tenant ID’ and ‘Environment ID’.

🙂

[Quick Tip] Power Automate flow | Get current Environment ID

Lets learn how to fetch the current ‘Environment ID’ in Power Automate flows.

What is Environment Id:

  • In Power Platform, Environment is a space to store, manage, and share your organization’s business data, apps, chatbots, and flows.
  • Every Environment will be associated with an unique identifier.

In few scenarios, we would need ‘Environment ID’ to perform actions. Lets see how to fetch the current Environment ID in Power Automate Flow.

Power Automate Flow to fetch current Environment ID:

  • Create a ‘Manually trigger flow’ with a Compose action.
  • Write the expression workflow()[‘tags’][‘environmentName’]
  • Save and Test the flow.
  • You will find the ‘Environment ID’ in the Compose action ‘Outputs’.

😉

[Experimental Feature] Call Dataverse actions directly in Power Fx

March 1, 2023 1 comment

In one of my previous articles, I’ve explained the steps to call the Dataverse Custom API from Canvas App using Power Automate cloud flow.

Recently a new experimental feature Call Dataverse actions directly in Power Fx released using which makers can now directly invoke a Dataverse Custom API within a Power Fx formula.

About the feature:

  • Power Apps will support the ability to directly call synchronous Dataverse actions without having to create a Power Automate Flow.
  • For apps that need to call many Dataverse actions, this will provide a significant performance boost.
  • It’ll be much easier to directly call Dataverse actions from the new Power Fx language element Environment.
  • The Environment object allows authors to dot into actions available in the environment. 
  • Its currently an Experiment feature and can be enabled from Canvas Studio -> Settings -> Upcoming features -> Experimental. Turn on the ‘Enable access to Microsoft Dataverse actions’ toggle.

In this article lets learn how to call the Custom API directly in Power Fx formula without the need of Power Automate flow. But first lets get familiar with our Custom API.

Custom API Details:

  • I already have a Custom API by name ‘cat_onboardcustomer‘ (Refer article for steps to create Custom API from scratch).
  • cat_onboardcustomer‘ API has a request Parameter by name ‘cat_customername‘ of type ‘String’.

Now that we know our Custom API, lets learn how to call the API in Power Fx by building a simple canvas app.

Steps to call Dataverse actions directly from Power Fx:

  • Create a Canvas App.
  • Open Settings -> Upcoming features -> Experimental. Turn on the ‘Enable access to Microsoft Dataverse actions’ toggle.
  • By enabling, A new Power Fx ‘Environment’ language object will be available under ‘Data sources’.
  • From the Canvas App, search for the ‘Environment‘ data source as shown below.
  • Double click and add the Environment data source.
  • Add a button on to the Canvas App and ‘OnSelect’ event, write the following Power Fx formula.
    • In the formula, we are calling cat_onboardcustomer API using ‘Environment’ data source.
    • We are also passing cat_customername request parameter in json format.

Environment.cat_onboardcustomer({cat_customername: "Calling API from Power Fx is awesome"})
  • That’s all needed. Play the App and click the button and you should see the ‘Account’ created with the name “Calling API from Power Fx is awesome”.

🙂

Categories: Canvas Apps Tags: ,

[Quick Tip] Dataverse Web API | Handling special characters in Filter query

February 28, 2023 1 comment

In this quick tip, lets see how to handle special characters while querying the Dataverse Web API.

Assume that you have a Dataverse Team with name CoE&Feb&Makers as below and you would want to query the Team by ‘Team Name’ using Dataverse Web API.

Query Dataverse Web API:

  • When you hit the URL, you will get an exception that ‘The query parameter is not supported‘. This is because the team name CoE&Feb&Makers is having & symbol, which is not supported.
  • To fix this, you need to encode the team’s name. You can use online editor to encode, if you are querying the Web API from browser.
  • Now use the encoded value CoE%26Feb%26Makers in the Filter and you should get the result.

🙂

Canvas App | Nested Galleries using polymorphic lookup Filter

February 27, 2023 Leave a comment

In this article, lets learn how to apply a filter on polymorphic lookup in Canvas App.

What is a polymorphic lookup in Dataverse:

  • Dataverse supports polymorphic lookups, which can refer to a record from any table in a set.
  • For example, ‘ParentCustomerID‘ (i.e., Display name is ‘Company Name’) column in the Contact table is a polymorphic lookup, where you can select either an Account or Contact record.
  • In below example, there are Contacts linked Accounts via ‘Company Name’ polymorphic lookup.

Now that we know what is a Polymorphic lookup in Dataverse, lets learn how to apply filters on polymorphic lookup in Canvas app by building a simple application.

Build a Canvas App using Nested Galleries and polymorphic lookup Filter:

In this sample Canvas App, I am going to add a ‘Contacts’ gallery inside an ‘Accounts’ gallery and show only those ‘Contacts’ linked to the ‘Account’ in the gallery row. Lets get started.

  • Create a new Canvas App.
  • On the form, add a Blank Flexible Height Gallery control ‘galAccounts‘ to the form and set Datasource as ‘Accounts’ Dataverse table.
    • You can add any Gallery type of your choice but the Flexible Height Gallery control’s row height auto expands as per the row’s content size.
  • Now, select the galAccounts and add a new gallery ‘galContacts‘ pointing to ‘Contacts’ Dataverse table as shown below.
  • This is how you configure nested galleries, in our case we have the ‘Contact’ gallery (i.e., galContacts) inside the ‘Accounts’ gallery (i.e., galAccounts).
  • Preview the App and we would get all the available Contacts in Dataverse in each row of the galAccounts.
  • But this is not what we want. We need to see only those Contacts linked to Account (i.e., ‘Contoso 1’ account row should only show ‘Contoso 1 – Contact 1’, ‘Contoso 1 – Contact 2′ and ”Contoso 1 – Contact 3’ contacts as per our available data).
  • Lets solve the final piece of the puzzle of applying the filter on ‘Contacts’ by ‘Company Name’ column which is a polymorphic lookup.
  • Select the galContacts gallery control, and in ‘Items’ property write following formula.
Filter(
    Contacts,
    AsType(
        'Company Name',
        [@Accounts]
    ).Account = ThisItem.Account
)
  • Now play the App and you should see the filtered Contacts.
  • You can also add a toggle button to show/hide the galContacts for collapse effect.
More about the formula:
  • If you notice, the formula has AsType function. Because the ‘Company Name’ column in the Contact table is a polymorphic lookup, which accepts either an Account or Contact record, we have to use AsType function to specify the table type we are filtering.
  • In our scenario, we need to filter ‘Contacts’ based on ‘Company Type’ column which holds a specific Account from galAccounts gallery.
  • So in the Filter formula we have used AsType function with 1st parameter as ‘Company Name’ and 2nd parameter as [@Accounts] which refers to the ‘Account’ TableType.

We also have IsType function, which tests whether a record reference refers to a specific table type. The function returns a Boolean TRUE or FALSE.

  • Following formula checks whether the ‘Company Name’ column of Contact table holds either an Account record or Contact type record.
If( IsType( ThisItem.'Company Name', Accounts ),
    AsType( ThisItem.'Company Name', Accounts ).'Account Name',
    AsType( ThisItem.'Company Name', Contacts ).'Full Name'
)

🙂