Friday, July 31, 2020

D365 CE Tips: Determine Mobile or Web in D365 CE using JavaScript

If you work with both CE Web and Mobile using Unified Interface, you may need to distinguish mobile client and web client inside your custom JavaScript. For example if you use single web resource for both CE web and CE mobile you have to distinguish both platforms to change your logic according to the platform.

Following code snippets will help you to distinguish platform based on the scenario.

Using GlobalContext

var globalContext = Xrm.Utility.getGlobalContext();
var Client = globalContext.client.getClient();


Using ExecutionContext

var Client = executionContext.context.client.getClient();

Value of Client variable will be “Web” or “Mobile” based on the platform. Hope this tip will be help. Enjoy 😊

Saturday, July 18, 2020

Resco Tricks: How to Fix Offline HTML Files not Updating issue in Resco Woodford Projects

If you use derived (hierarchical) Resco Woodford projects you may experience some time files are not updated even after you published all solutions. This article will help you to identify possible issues and resolve them.

For more details about Resco Woodford derived projects please refer following link


Scenario:
We have derived project structure like following
MSFieldService -> Survey -> ClientCustomizations


We have offline html file called “ReadOnlySurvey.htm” in survey solution. Due to an urgent production issue, development team replaced that offline html file directly in client customization Woodford project (top most project). However, in next production release development team deploy Survey Woodford project but after deployment they observed “ReadOnlySurvey.htm” file didn’t update and it did not contains all the changes did in development phase.


How to fix this issue:
Once offline html file manually override in the top most project without its own project, Woodford did not apply changes came through its original project any more. To apply changes from root project to top most project, files should be in “Inherited” state not in “Overridden” state.


To fix that before you import your root project (Survey project) to Resco, you have to restore the files you overridden before.
First you should open Woodford project which contains file you overridden before (in our case “ClientCustomization” project). Then select the relevant files and click restore button in the Woodford ribbon and save. Then system will restore the file from its root project. Then you can import your project (in our case Survey project) and publish them all.


This will fix your issue and apply changes to relevant files.

Monday, July 6, 2020

Unit Testing Dynamics 365 CE (CRM) Plugins/Actions

Unit testing is very important concept which ensure your code work without error and fulfill the requirement. Unit testing CRM plugins/actions are challenging because plugins and actions are executing under CRM context. If you create unit test project against CRM plugin project you have to mock CRM plugin context to unit test. Mocking CRM plugin context is not an easy task because it need lots of coding and it will be adding additional burden to the development.

There is a tool called “FakeXrmEasy” (https://dynamicsvalue.com/home) will remove that extra burden from development team and fake the CRM context. Using fake Xrm context we can unit test CRM plugins and actions without deploying them to CRM. This will help to debug CRM plugins as well.

Unit testing using FakeXrmEasy
I have create plugin project called “CEAutomation” and MSTest unit test project called “PluginUnitTest”.



I used MSTest (.Net Framework) as unit test project but there are several unit test frameworks available like NUnit, xUnit, etc.



Once you create unit test project you have to add FakeXrmEasy assemblies as NuGet Package. It will add Core CRM assemblies and necessary NuGet packages to your project.

Note: Please make sure you add correct version of FakeXrmEasy because there are several NuGet packages available for different versions of CRM.

In my case I use D365 CE online version, due to that reason I installed CRM 365 version supported version.


Test Scenario:
We have an online exam system which conduct exams through internet. After exam each and every candidate should fill out survey which collect their feedback about exam. Once they complete the survey, system will extract data for report. We implement CRM action to extract survey responses by sending survey id as an input parameter. Once action invoked with valid survey id, it query the CRM and retrieve all responses then convert responses to JSON string and return as output parameter.

We are going to create unit test for that CRM action. We are going to use FakeXrmEasy to create unit test and use XrmRealContext to connect to the CRM.

If our plugin implemented correctly once we feed valid CRM survey record id it should return the available responses list as JSON.

Plugin Code:

namespace CEAutomation.Response
{
    public class ResponseHandler : IPlugin
    {
        public IServiceProvider ServiceProvider
        {
            get;

            set;
        }

        public IOrganizationService OrganizationService
        {
            get;

            set;
        }

        public IPluginExecutionContext PluginExecutionContext
        {
            get;

            set;
        }

        public ITracingService TracingService
        {
            get;

            set;
        }

        public void Execute(IServiceProvider serviceProvider)
        {
            // Obtain the execution context service from the service provider.
            PluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            // Obtain the tracing service from the service provider.
            TracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            // Obtain the Organization Service factory service from the service provider
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

            OrganizationService = factory.CreateOrganizationService(PluginExecutionContext.UserId);

            if (PluginExecutionContext.InputParameters.Contains("SurveyId")
&& Guid.TryParse(PluginExecutionContext.InputParameters["SurveyId"].ToString(), out Guid surveyId) 
&& surveyId != Guid.Empty)

            {
                Survey.Survey survey = new Survey.Survey();
//Do What you want
                var responses = survey.GetSurveyResponsesJsonString(OrganizationService, TracingService, surveyId);
                PluginExecutionContext.OutputParameters["Responses"] = responses;
            }
        }

    }
}


Adding App.config to unit test project

Visual Studio not allowed to add Application Configuration file (App.config) file to unit test. We need to add it manually because we need to provide CRM connection string to XrmRealContext create connection. Since our project library does not contains App.config file we have to create it then include it to project using visual studio and set property to copy it to output directory. Add connection string as following and set copy to output directory property true in file properties.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="SurveyCRMDevInstance" connectionString="AuthType = Office365; Url = https://org.crm.dynamics.com/;Username=testusername@testdomain.onmicrosoft.com;Password=password" />
  </connectionStrings>
</configuration>


We named our CRM instance connection string as

SurveyCRMDevInstance”. You have to use same name when you initiate XrmRealContext.
To invoke plugin we have to supply survey id as input parameter named “SurveyId”. Once plugin executed it returns data as output parameter named “Responses”. In unit test we have to supply valid survey id as input parameter and we can extract result using output parameter.

Following code snippet will show how create unit test for action with input and output parameters.

[TestMethod]
public void RetrieveResponsesTest()
{
    try
    {
        var context = new XrmRealContext
        {
            ProxyTypesAssembly = typeof(ResponseHandler).Assembly,
            ConnectionStringName = "SurveyCRMDevInstance"
        };

        Guid.TryParse("9eab7684-fbb5-ea11-a812-000d3a305386", out Guid conductedSurveyId);

        var executionContext = context.GetDefaultPluginContext();
        executionContext.MessageName = "ResponseHandler";
        executionContext.InputParameters = new ParameterCollection
        {
            new KeyValuePair<string, object>("SurveyId", conductedSurveyId)
        };
        context.ExecutePluginWith<ResponseHandler>(executionContext);
        string outputString = executionContext.OutputParameters["Responses"].ToString();

        if (string.IsNullOrEmpty(outputString))
        {
            Assert.Fail("No result");
        }
        else
        {
            List<ebecssvy_responses> responseObjList = Utility.JsonToObject<List<ebecssvy_responses>>(outputString);

            Assert.AreNotEqual(0, responseObjList.Count, "List Contains Data");
        }

    }catch (Exception ex)
    {
        Assert.Fail(ex.Message);
    }
}



This can extend to unit test any plugin/action/custom workflow. We will post those special scenarios in future posts.
Happy Coding 😊