Role based Canvas Apps

The inspiration for this post is this post from Geetha -> https://svaghub.wordpress.com/2018/11/03/role-based-security-in-powerapps-using-spgroups/. The idea is to basically use Flow to check what kind of permission the current user has, so that some functionalities can either be lit up or hidden in the canvas app. In her post, Geetha was using SharePoint user group to get this information. Being from a CRM background, I want this to be retrieved from CDS + I am not a big fan of SharePoint.

Attempt 1: Use DataSourceInfo(‘Users’, DataSourceInfo.CreatePermission), to check if the current user can create Users, which means they are Sys Admin. But, there seems to be a limitation on DataSourceInfo, which means it does not return the correct permissions for CDS entities. It always returns true, which is not correct. So, this attempt was not successful. This would have so much easier if it had worked, as all the logic would be entirely in the canvas app.

Attempt 2: Flow magic. I didn’t want to head this route, but since DataSourceInfo did not work, I had to use Flow to solve the problem. Solution is one of the restricted CDS entities. Only certain roles have access to read this entity OOB. Below are those roles.

Privileges.png

So, I am using this as the flag to show or hide canvas apps controls. Below is my Flow.

Flow.png

There are two parallel branches, one if there is an exception while retrieving Solution records, which means that the user cannot read “Solution” entity and another branch if they have read privilege on “Solution” entity. Depending on which branch the Flow takes, “canread” can be true or false. I can use the result to show or hide controls on the canvas app. The two branches have the appropriate “configure run after” set.

configure run after.png

Branches.png

The clunky bit about this, which I don’t like is that fact that Flow will report that the execution has failed, when Flow takes the “Cannot read Solutions” branch.

Flow result.png

In the programming world, handling an exception appropriately and continuing like nothing happened, is not considered a failure, but it looks like Flow has a different opinion on this.

Potential improvement to the design: Create a new CDS entity called “Canvas App Permission” and create attributes on this entity, to manage which areas in Canvas App should be shown or hidden. Create multiple records of this new entity and assign this to teams or individual users, depending on how you want the permissions to be applied in canvas apps. The Flow can then retrieve this entity, and PowerApps can use this result of the Flow to hide/show areas or functionality.

Hope this is useful. Credit to Geetha for coming up with the original idea.

Advertisements

Building Spirograph using PowerApps

Over the past few weeks, people have been demonstrating some cool games, built entirely using PowerApps. The reason being, that there was a contest from ThoseDynamicsGuys. If you are interested in finding out about some interesting games from this contest, watch this video from Mr.Dang himself -> https://www.youtube.com/watch?v=0-ZWqs_emQA where he reviews the games.

There were some apps that caught my eye during this period:

  1. Power Flappy by Scott Durow
  2. Power Roulette by Geetha Sivasailam
  3. Coin Dog by Makato Maeda
  4. Air Resistance by Nagao Hiroaki

I was especially fascinated by what Scott and Makato accomplished with the side scrolling nature of the games, how Geetha managed to rotate the roulette as there is no rotate angle property on images and how Nagao managed to calculate the trajectory and resistance of the ball. The first step to learning is to understand how other people do it, so I spend 4-5 days to understand how these apps have been developed.

I then wanted to develop something of my own, using the concepts that I had learnt. Spirograph was the first thing that I thought of. The easiest part was to get the equations to calculate x and y. I had to then learn the basics of starting and stopping a timer, and how to create a sense that the pattern was being “drawn”.

Since image control can render SVG, I decided to try this approach. My first challenge was how do I calculate the x and y on every tick of the timer. So, I decided to follow the approach demonstrated by Brian Dang, that involve checking and un-checking a toggle control.

Every time the toggle is checked, I can increment the iterator variable and calculate x and y for the line to be drawn and the string for the SVG path with all the lines. In SVG you can draw a line, by moving to a location specifying Mxy and then drawing the line using Lxy. Below is the formula in the OnCheck event of the slider

Set(VarI, VarI+1);
UpdateContext({x:
(((RSlider.Value-rSlider.Value) * (Cos(rSlider.Value*VarI/RSlider.Value))) + (aSlider.Value * Cos(VarI*(1-(rSlider.Value/RSlider.Value))))),
y:
(((RSlider.Value-rSlider.Value) * (Sin(rSlider.Value*VarI/RSlider.Value))) - ((aSlider.Value * Sin(VarI*(1-(rSlider.Value/RSlider.Value))))))
});
Collect(Lines, {Row: VarI,
X: x,
Y: y});
Set(PathVar, PathVar & If(PathVar="", "M", "L") & x & "," & y);
true

I can then simply set the SVG path’s d property from the PathVar variable, that has all the line co-ordinates. Below is the value for the Image property of the Image control that renders the Spirograph.

SVG Path.png

Below a video of the app in action. You can play around with the sliders and be fascinated by the patterns that it generates.

Spirograph.gif

Here are some interesting patterns the app generated.

You can download the app from the PowerApps Community Gallery -> https://powerusers.microsoft.com/t5/Community-Apps-Gallery/Spirograph/m-p/175447

References:

SVG Paths – https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#Line_commands

Math behind Spirograph – http://www.mathematische-basteleien.de/spirographs.htm

Building a MVP Notifier using Flow and Azure Functions

Since MVPs are now announced every month, I have found it hard to track down the new awardees in Business Applications area before 3rd of every month. So, I thought I will build a notifier using Flow and Functions. This is the logic:

  1. Seed the inital MVP data from mvp.microsoft.com, to figure out the new MVPs every month into Azure Tables
  2. Schedule the Flow at 1st of every month at 5 p.m. PDT. Hopefully by this time, everyone has filled out atleast their name in the MVP profile.
  3. Retrieve the MVP data again from mvp.microsoft.com
  4. Figure out the new MVP and post a message to Slack. Add the new MVP details to Azure Tables

The logic that retrieves the details from mvp.microsoft.com uses Azure Functions. Below is the project.json for that Function App.

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "AngleSharp": "0.9.10",
        "Newtonsoft.Json": "11.0.0.2"
      }
    }
   }
}

Next, is the actual logic that returns MVP list for the category passed in the URL.

using System.Net;
using AngleSharp;
using AngleSharp.Dom;
using AngleSharp.Dom.Html;
using System;
using System.Runtime.InteropServices;
using System.Text;
using Newtonsoft.Json;

public static async Task Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");

    // parse query parameter
    string mvpCategory = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "category", true) == 0)
        .Value;

    if (mvpCategory == null)
    {
        // Get request body
        dynamic data = await req.Content.ReadAsAsync();
        mvpCategory = data?.category;
    }

	var pageNumber = 1;
	var webClient = new WebClient();
	IHtmlDocument doc;
	IHtmlCollection mvpProfiles = null;
	List mvps = new List();
	var htmlParser = new AngleSharp.Parser.Html.HtmlParser(new Configuration().WithDefaultLoader());
	do
	{
		var data = webClient.DownloadData(new Uri($"https://mvp.microsoft.com/en-us/MvpSearch?ex={WebUtility.UrlEncode(mvpCategory)}&sc=s&pn={pageNumber++}&ps=48"));
		doc = htmlParser.Parse(Encoding.UTF8.GetString(data));

		mvpProfiles = doc.QuerySelectorAll(".profileListItem");
		var currentPage = mvpProfiles
		.Select(d =>
		{
			var nameAndLocation = d.QuerySelector(".profileListItemFullName a");
            var mvpUrl = nameAndLocation.GetAttribute("href");
			return new MVPInfo
			{
				Name = nameAndLocation.TextContent,
				Url = $"https://mvp.microsoft.com{mvpUrl}",
				Country = d.QuerySelector(".profileListItemLocation .subItemContent").TextContent,
                Id = mvpUrl.Substring(mvpUrl.LastIndexOf("-")+1)
			};
		})
		.ToList();
		mvps.AddRange(currentPage);
	} while (mvpProfiles.Any());

    var json = JsonConvert.SerializeObject(mvps, Formatting.Indented);
    return mvpCategory == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a MVP category on the query string")
        : new HttpResponseMessage(HttpStatusCode.OK)
          {
                Content = new StringContent(json, Encoding.UTF8, "application/json")
          };
}

public class MVPInfo
{
	public string Name { get; set; }
	public string Url { get; set; }
	public string Country { get; set; }
    public string Id { get; set; }
}

It uses the AngleSharp library to parse the response returned by mvp.microsoft.com search.

Below is the Flow that uses this Function, to populate the Azure Table.

Flow Overview.png

As you can see below the Flow runs at 5 p.m on 1st of every month, and gets the current MVP list from Azure Table, MVPAwards.

Azure Tables.png

The partion key for the table is month and year and the row key is the actual MVP Id.

Azure Tables Definition.png

Since this Flow and the associated Function were built for my personal use, I call the Function directly without using a custom connector. I pass the award category on the URL of the Functions itself. If you want to use a custom connector, refer one of my earlier posts.

Azure Function Call.png

After parsing the JSON returned by the Azure Function, I can use the “select” function to map the returned data, so that I can compare the data based on the MVP Id and insert the new MVPs into the Azure Table, if needed. The partion key will the current year and month (yyyyMM).

Flow Select.png

Next is the crucial bit, where I do the actual comparison.

Compare MVPs.png

The “filter” function can be used to see whether there are any matches in MVP list that was retrieved from Azure Table i.e. the previous month MVPs. If no results were returned, that means they were not a MVP last month. So, they are a new MVP and this can be stored in Azure Tables and can also be posted to Slack.

IF Then Logic.png

Post to Slack is using the Webhooks registered on the channel.

Slack Web Hooks.png

After the Flow has finished running, the output gets posted to Slack.

MVP awards.png

References:

  1. https://powerusers.microsoft.com/t5/Building-Flows/Comparing-File-Lists/td-p/64178
  2. https://api.slack.com/incoming-webhooks

Improving entity forms using embedded PowerApps

I have been looking into scenarios with PowerApps and Flow that can benefit Dynamics 365 Customer Engagement user experience. One of the scenarios that can add value right away is on the entity forms. PowerApps can be embedded as an IFrame on the normal entity forms, and can be used similar to Dialogs to offload some processing to PowerApps and Flow.

Here is the finished product.

Embed PowerApps in UCI

This works without any JavaScript at all in UCI, with the normal IFrame control on the form. Make sure to tick the option that passes the record id and objecttype code and untick cross site scripting restriction.

IFrame Properties.png

No scripts are needed on the form to embed the PowerApp.

Form Scripts

Once PowerApps is in place, the current form context can be inferred using the “id” parameter that is passed on to the form.

PowerApps Initial

I use a known Guid during the design phase to assist me with the app design process, as the PowerApps calls the Flow during the OnStart event and sets the ProblemDetails variable.

A Flow can be associated to an event, from the Action->Flows area.

Associate Flow.png

When the PowerApps loads, it calls the Flow with the Guid, to retrieve the case details. The Flow that responds to PowerApps with these details on the case: Title, Customer Name, Type of Customer (Account or Contact).

Case Details Flow.png

In this Flow I just use “Respond to PowerApps”action and return the three outputs.

Return to PowerApps.png

I used variables to store the Client, which could be the Account’s name or Contact’s FullName, depending on what is on the case. The client type could be either Account or Contact. Account Details and Contact details are retrieved based on the result of the Client Type branch.

For the second Flow, the user presses the “Check” button which performs some additional checks based on business criteria. For this Flow, I used the “Response” action, which allows me to return JSON results. I stored the cases I am interested in on an array variable.

For each case.png

From the variable, I used Select action to grab only the properties I am interested in.

Select.png

I can then use the “Response” action to return these to PowerApps.

Response.png

One weird thing that I encountered in PowerApps/Flow integration, is that I would simply see the result as “True” from Flow, when I tried to return the return the response straight from the variable.

True Response.png

When I used Parse JSON and then Select to reduce the properties it started working. This can happen when there is something wrong with the schema validation, but I am not sure how this can happen when I copy-paste the JSON response from the previous steps to auto-generate the schema.

One more thing: When the Flow that is associated with the PowerApps changes, just make sure to disassociate and reassociate the Flow. I had issues when I did not do this, due to PowerApps caching the Flow definition.

References;

https://preview.flow.microsoft.com/en-us/blog/howto-upload-return-file/

https://docs.microsoft.com/en-us/powerapps/maker/canvas-apps/get-sessionid

 

 

 

Using Virtual Entities to query metadata

After my previous post, I continued to explore virtual entities to see what real life problems I can solve using them. One problem I could think of was metadata. How awesome would it be, if I can use Advanced Find to query entity and attribute metadata, or visualise them as a normal entity! It is not a dream anymore. I have developed an open-source solution to do this.

Here is some of the sample queries:

Query all attributes of type customer

Query by Attribute Type

Query by Attribute Type Results

Query all mandatory attributes

Query All Required.png

Query All Required Results.png

Query by Attribute Type and Entity

Query by Attribute Type and Entity.png

Query by Attribute Type and Entity Results.png

Query all Many to Many intersect entities

Query MM Entity.png

Query MM Entity Results

Query entities that have quick create enabled

Query Quick Create

Quick Create Results

You can open the entity and see more details.

Entity Form

You can open the attribute and view more details as well.

Attribute Form

All this is awesomeness is possible using the power of virtual entities. There are two virtual entities that you can query. They are called Entity and Attribute.

VE Solution

You can download the source code and managed solution from https://github.com/rajyraman/Metadata-Virtual-Entity/releases.

This step is important

After importing the managed solution,  change the data source for the attribute entity from “Entity Datasource” to “Attribute Datasource”. You have to do this from the Customization area and not from the managed solution.

Customise.png

Change Datasource.png

This is because by default, the system does not allow relationships between two virtual entities that have different datasources. This exception is shown when you try to do this.

Solution Exception

Stack Trace.png

In order to workaround this exception, you keep the data source same for the “Entity” virtual entity (parent) and “Attribute” (child) virtual entity, create the relationship and then change to the right datasource. Hence, the managed solution has the datasource set to “Entity Datasource” for the “Attribute” virtual entity, which has to be changed manually after importing the solution.

I hope this solution would be really useful for administrators. Please let me know any feedback on the post or on GitHub issues.

Sourcecode: https://github.com/rajyraman/Metadata-Virtual-Entity

Managed Solution: https://github.com/rajyraman/Metadata-Virtual-Entity/releases/latest

 

 

Virtual Entities for tracking recently used items

Virtual entities is a powerful feature that can be used not only to bring data from external sources, but also from inside Dynamics CRM/Dynamics 365 Customer Engagement.

Jason Lattimer already has a post (https://jlattimer.blogspot.com.au/2017/12/creating-custom-virtual-entity-data.html) that goes through how to setup the custom datasource/data provider. So, go and read that first as it has all the screenshots and I would be duplicating the content, if I go through the steps again.

Gotcha 1:

There is a exception when you create the datasource.

Datasource Creation Error

You can simply ignore this and refresh the Plugin Registration tool.

Gotcha 2: If you don’t want the user the open up an individual record, you don’t have to implement Retrieve message. It is optional. Since, I just want a collated entity, I did not register any plugin for the Retrieve.

Data Provider

Gotcha 3: You have to open up the newly created Data Provider entity, and enter the external name. If you don’t enter this, you will be unable to create the data source, as it will always error out.

Data Source Primary Key Attribute

Create New Data Source

Data Source List

Objective: MRU items should be accessible from Advanced Find. As an Administrator, I would like to query this data, and see metrics around user participation, entity usage, activity by week/month/year etc.

This is the Most Recently Used area.

Recent Items

This is the Advanced Find on the virtual entity, which is driven by the same data.

Advanced Find Results

As you can see, the data matches up. All the heavy lifting is done by the plugin, that retrieves the records from the “UserEntityUISettings” entity, parses the XML, sorts by user and accessed on and then populated the virtual entity “Recent Items”.

You can query by “Type Equals”, “User Equals” and “User Equals Current User”.

Advanced Find

I can also do a PowerBI report that is driven by the same virtual entity.

PowerBI Dashboard

Source code -> https://github.com/rajyraman/Recent-Items-Virtual-Entity

Managed Solution -> https://github.com/rajyraman/Recent-Items-Virtual-Entity/releases

I hope this helps people to use virtual entities to retrieve data from inside CRM as well – a sort of collation mechanism for reporting.

Reference:

https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/virtual-entities/sample-generic-ve-plugin

https://jlattimer.blogspot.com.au/2017/12/creating-custom-virtual-entity-data.html

Integrating Slack to Dynamics 365 Customer Engagement

In the previous post I described how easy it is to use Microsoft Flow to interact with Dynamics 365 Customer Engagement, by letting Azure Functions handle the core logic. In this post, I will show how to integrate Slack to Dynamics 365 Customer Engagement using Flow and Functions.

This is the objective: In my Slack channel, I quickly want to query the record count using a slash command, without have to jump into XrmToolBox/Dynamics 365 Customer Engagement application itself. I took the record count as a simple use case. You can create multiple slash commands, with each one doing a different targeted action in Dynamics 365.

The first step is to create the new app in Slack. Navigate to https://api.slack.com/apps/new

New Slack App.png

Since this is an internal app that I won’t be distributing, I am choosing a simple name. If you plan to distribute this app, choose a more appropriate name.

Now you will be taken to the app’s initial config screen.

New App Initial Screen.png

We will be creating a new slash command that will return the record count of the entity from Dynamics 365 Customer Engagement. Click on “Create a new command”

Slash Commands.png

Choose the name for the slash command. I am just going with “/count”.

Add new slash command.png

 

The critical part here is the Request URL. This the URL that Slack will POST to with some information. What is the information and how does this look like? I used RequestBin* (see footnote) to find out this information.

Request Bin.png

 

Note the two relevant parameters:

  • command – This the actual slash command the user executed
  • text: This is the text that comes after the slash command

For e.g., if I typed “/count account” into the Slack chat window, the command parameter’s value will be “/count” and the text parameter’s value will be “account“. During the development phase, I put in the RequestBin’s URL in the Request URL. We will come back later, once the Flow is complete and replace this placeholder URL, with the actual Flow URL.

Now you can see the list of slash commands in this app.

List of slash commands.png

Now click the “Basic Information” screen on the left, and then on “Install your app to the workspace”. This should expand the section, and you can now actually install the app into your workspace by clicking on “Install App to Workspace”.

Slack App Information.png

Grant the required permissions for the app.

Authorise App.png

Now it is time to develop the Flow, which looks very similar to my previous post about Flow and Functions. The difference here is, that the Flow is triggered by HTTP POST, and not manually using a Flow button. Flow will receive the slash command from Slack. Here is what the Flow looks like.

Flow Execution Log

Here is what the Flow does:

  1. When HTTP POST request is received from Slack, it posts a message back to Slack asking the user to wait while the record count is retrieved.
  2. Checks if the slash command is “count”
  3. If the slash command is “count”, call the Azure Function using the custom connection (refer previous post, on how to do create a custom connection to the Azure Function that you can use in Flow)
  4. Parse the response received from Azure Function, which queries Dynamics 365 Customer Engagement for the entity’s record count
  5. Send a mobile notification that shows up if the user has Flow app installed
  6. Send a message back to the channel that the slash command was executed on, with the record count

There are three important bits in the Flow:

The first is getting the slash command from the POST message.

Parse command.png

The second is posting into the right Slack channel i.e. the channel that was the source of the slash command. You can get the channel from the “channel_name” parameter.

Post message step.png

The third is parsing the JSON returned by the Azure Function. This is schema of the JSON returned.

{
    "type": "object",
    "properties": {
        "entityName": {
            "type": "string"
        },
        "count": {
            "type": "number"
        }
    }
}

You can get the Flow URL by clicking on the HTTP step that is the first step of the Flow.

Flow URL.png

Grab the whole HTTP URL and plug it in on the slash command’s request URL.

Now, you can use the slash command on your workspace to get the record count.

Slack WorkspaceSlack Workspace result

Note: When I worked on this post last month, RequestBin had the capability to create private bins. But, when I looked into this again this week it looks like they have taken away this capability, due to abuse -> https://github.com/Runscope/requestbin.

Request Bin message.png

You would have to self-host to inspect the POST message from Slack. The other option is to create the Flow with just the HTTP request step and look into the execution log, to see what was posted like below.

HTTP Post.png