A recent requirement for a custom web resource gave me a chance to create a very simple example of a Dynamics 365 Web Resource that leveraged Knockout and Custom Actions that I thought would be great to share with anyone who has a similar use case.
The requirement was to build a dashboard that displayed the status of certain integrations with Dynamics 365 and data related to the integrations status. I went online to find an open source HTML template that had elements that looked like this.

Ultimately, what I needed to accomplish was populating these individual blocks with information from server-side integrations in Dynamics 365. To accomplish this I created a custom action. As you can see the code is pretty straight forward. In the example below I’m just dummying up a collection of MonitoringResponse objects that would ultimately be populated with the actual values from integrations.
using Integration.Plugins.Responses;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;
using System;
using System.Activities;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Text;
namespace Integration.Plugins
{
public class BaseActions : CodeActivity
{
[Output("Response")]
public OutArgument<string> Response
{
get;
set;
}
protected override void Execute(CodeActivityContext context)
{
// Getting OrganizationService from Context
var workflowContext = context.GetExtension<IWorkflowContext>();
var serviceFactory = context.GetExtension<IOrganizationServiceFactory>();
var orgService = serviceFactory.CreateOrganizationService(workflowContext.UserId);
//Dummy up some responses and add them to a collection
List<MonitoringResponse> responses = new List<MonitoringResponse>();
MonitoringResponse response1 = new MonitoringResponse
{
SystemDisplayName = "System 1",
SystemLatency = "10 ms",
SystemStatusCode = "200"
};
responses.Add(response1);
MonitoringResponse response2 = new MonitoringResponse
{
SystemDisplayName = "System 2",
SystemLatency = "5000 ms",
SystemStatusCode = "500"
};
responses.Add(response2);
MonitoringResponse[] responseArray = responses.ToArray();
using (MemoryStream stream = new MemoryStream())
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(responseArray.GetType());
serializer.WriteObject(stream, responseArray);
string result = Encoding.Default.GetString(stream.ToArray());
//get JSON data serialized in string format in output parameter
Response.Set(context, result);
}
}
}
}
Simple enough. Once registered in Dynamics 365 I created an Action from the Dynamics 365 user interface to make use of my custom action code. Here’s what that looks like.

The first step simply calls the custom action code we registered and the second step sets the ‘Response’ output parameter on the Action to the ‘Response’ output parameter from the call to the custom code.
The next step was to use this custom action with a Web Resource and bind the results to my dashboard elements. To do this I leveraged the Knockout.js library to make the client side implementation as simple as possible. There were a number of css web resources and js resources that I needed to include in my solution to get this to work and you can see what they are specifically in the Github repo, but the important parts are contained in 2 files intmon.js and IntegrationMonitor.html. The former is responsible for making the call to my custom action and saving the results to a variable for the html page to access and databind to the markup. First here’s what the intmon.js looks like
function IntegrationMonitorModel() {
var self = this;
self.pingResults = ko.observableArray([]);
// methods/functions
self.callAction = function (action, callback, errHandler) {
var serverURL = Xrm.Page.context.getClientUrl();
var req = new XMLHttpRequest();
// specify name of the entity, record id and name of the action in the Wen API Url
req.open("POST", serverURL + "/api/data/v9.0/" + action, true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200 && this.response != null) {
var data = JSON.parse(this.response);
callback(data);
}
else {
if (this.response != null && this.response != "") {
var error = JSON.parse(this.response).error;
errHandler(error.message);
}
}
}
};
// send the request with the data for the input parameter
req.send(window.JSON.stringify(data));
}
self.callAction('mf_PingIntegrations',
function (data) {
self.pingResults.removeAll();
//console.log(data);
if (data.Response.length == 0) {
// no records returned
}
else {
var responses = JSON.parse(data.Response);
if (responses.length > 0) {
ko.utils.arrayForEach(responses, function (d) {
self.pingResults.push(d);
});
};
}
},
function (err) { }
);
};
The first thing we do here is create our array of objects that we are going to use to databind to our markup. Then we add a method definition called callAction for calling our custom action in Dynamics 365. Finally, we immediately call the callAction method with the unique name of the custom action we created in the Dynamics 365 UI (in this case ‘mf_PingIntegrations’). After the custom action is called we store the values returned in the output parameter in our collection variable to be used for databinding our markup. That’s it. The only thing left to do is create our html markup and databind the results of our custom action to be displayed.
The html, like the server side and client side code, is very straight forward which made this particular project a good example to share. Let’s take a look.
<html>
<head>
<title>Integration Monitor</title>
<meta charset="utf-8">
<script src="../../ClientGlobalContext.js.aspx" type="text/javascript"></script>
<script src="../scripts/jquery.1.11.min.js" type="text/javascript"></script>
<script src="../scripts/bootstrap.min.js" type="text/javascript"></script>
<script src="../scripts/knockout.3.4.2.js" type="text/javascript"></script>
<script src="../scripts/SDK.REST.js" type="text/javascript"></script>
<script src="../scripts/json2.js" type="text/javascript"></script>
<script src="scripts/intmon.js" type="text/javascript"></script>
<link href="../css/bootstrap.min.css" rel="stylesheet">
<link href="css/intmon.css" rel="stylesheet" type="text/css">
<script type="text/javascript">
$(document).ready(function () {
ko.applyBindings(new IntegrationMonitorModel());
});
</script>
</head>
<body style="-ms-word-wrap: break-word; background-color:white">
<!--<div class="container" id="searchByTraits">-->
<div style="width: 100%; height:90%;">
<div class="app-body">
<main class="main" style="width: 100%; height:90%;">
<div class="container-fluid">
<div class="animated fadeIn">
<div class="row" data-bind="foreach: pingResults">
<div class="col-sm-6 col-lg-3">
<div class="card text-white" data-bind="css: { 'bg-primary' : SystemStatusCode == '200', 'bg-warning' : SystemStatusCode != '200' && SystemStatusCode != '500', 'bg-danger': SystemStatusCode == '500' }">
<div class="card-body pb-0">
<div class="text-value" data-bind="text: SystemDisplayName">System Name</div>
<div style="clear:both; float:left" class="text-value">HTTP Status:</div><div style="margin-left: 10px; float:left" class="text-value" data-bind="text: SystemStatusCode">Unknown</div>
<div style="clear:both; float:left" class="text-value">Latency:</div><div style="margin-left: 10px; float:left" class="text-value" data-bind="text: SystemLatency">Unknown</div>
</div>
<div class="chart-wrapper mt-3 mx-3" style="height:70px;">
<canvas class="chart" id="card-chart1" height="70"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
</body>
</html>
It may be a little difficult to read (I need to update my WP code plugin) but as I mentioned previously you’ll notice there are a number of script and css references that I’m importing as part of this document including our intmon.js. However the actual html markup is surprisingly simple. What we have in the body of the document is essentially and single div tag with the class=’row’ that includes the Knockout data-bind=”foreach: pingResults” that indicates we are going to repeat elements contained within this tag for each of the responses received from our call to the custom action. Within the row div we have another div for our columns (or in this case our individual status cards displayed in the dashboard). The cards are binding the values of the responses to div elements using the Knockout databinding syntax (e.g. data-bind=”text: SystemStatusCode”). Additionally, we are setting the css class on the card element using Knockout based on the SystemStatusCode returned by our custom action to show the different colors based on the status (i.e. green, red and yellow) using the data-bind=”css:..
Once I loaded these web resources into Dynamics 365 and embedded the html resource onto a Dashboard in Dynamics 365 I had the dashboard the customer was looking for and the only thing left to do was hook in the real integrations to the custom action code. Not too shabby. You can try it yourself by downloading the source on GitHub https://github.com/mikefactorial/IntegrationMonitorSample









Leave a Reply