Skip to content

Plugins

Peter McDonald edited this page May 22, 2023 · 16 revisions

Dataverse Plugins

Dataverse powertools has a number of features to assist with the management of plugins in Dataverse. These features include:

  • Creating and updating plugins, plugin steps, and custom workflow steps
  • Deploying plugins using spkl
  • Registering both plugin and workflow steps using spkl. This extension helps with the creation of the spkl decorations for plugin and workflow step registrations.
  • Xunit Tests utilising FakeXrmEasy for dataverse fakes.

Table of Contents

Getting Started

To get started with Plugins, you will need to initialise your project. This can be done by selecting Initialize Project from the command palette or side bar. This will create a instance of the Plugins template, restore dependencies and create a new Plugin class called sampleClass.ts as well as a a new Workflow class called sampleWorkflow.ts. The following options will now be available in the command palette or side bar:
Plugins Menu

Restore Dependencies

This option will restore the local dependencies onto your PC. This is done for you automatically as part of the Initialize Project command. However it will be needed if you add additional dependencies to your project or if you are working on a project that has been cloned from a repository.

Generate Early Bound

To generate Early Bound classes select Generate Early Bound from the command palette or side bar. This will create a new folder called generated within plugins_src and add the classes to this folder. Note: This will need to be done each time the underlying schema changes.

Create Plugin / Workflow Class

To create a new Plugin or Workflow class, right click on the plugins_src folder and select either Dataverse Powertools: Create Plugin Class or Dataverse Powertools: Create Workflow Class.

Create Plugin/Workflow Class

Visual Studio Code will then ask you for the name of the new class. This will be the name of the class that is created and the name of the file. Note: The name of the class must be unique.

Note: The plugins_src.csproj is configured to include all files within the plugins_src folder so no manual changes are required to the project file.

A sample of a new plugin class file is below:

using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
namespace PluginSRC
{
    // Sample decroation, this will register the below step as part of the publish. Decorations can be added manually or via the Add Plugin Decoration command.
    // They must appear above the class declaration and must be uncommented. A unique GUID is required for each decoration, the builtin command will do this for you.
    // [CrmPluginRegistration(MessageNameEnum.Create, "contact", StageEnum.PostOperation, ExecutionModeEnum.Synchronous, "", "PluginSRC - ClassName - Create", 1, IsolationModeEnum.Sandbox, Id = "90705ddd-1442-4403-8cb6-48807e2ecaf7")]
    public class ClassName : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService userService = factory.CreateOrganizationService(context.UserId);
            IOrganizationService systemService = factory.CreateOrganizationService(null);
            using (var userServiceContext = new XrmSvc(userService))
            using (var systemServiceContext = new XrmSvc(systemService))
            {
               // Do stuff
            }
        }
    }
}

A sample of a new workflow class file is below:

using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using System.Activities;
using Microsoft.Xrm.Sdk.Workflow;

namespace PluginSRC
{
    //[CrmPluginRegistration("WorkflowActivity","ClassName", "Workflow Description", "Workflow Group Name", IsolationModeEnum.Sandbox)]
    public class ClassName : CodeActivity
    {
        [RequiredArgument]
        [Input("Contact")]
        [ReferenceTarget("contact")]
        public InArgument<EntityReference> Contact { get; set; }

        protected override void Execute(CodeActivityContext executionContext)
        {
            ITracingService tracingService = executionContext.GetExtension<ITracingService>();
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
            IOrganizationServiceFactory factory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService userService = factory.CreateOrganizationService(context.UserId);
            IOrganizationService systemService = factory.CreateOrganizationService(null);
            using (var userServiceContext = new XrmSvc(userService))
            using (var systemServiceContext = new XrmSvc(systemService))
            {
               // Do stuff
            }
        }
    }
}

Building Plugins

Plugins will be build and released in Debug mode when using VSCode. As part of the Azure DevOps Plugin build pipelines the plugins are rebuilt and saved as artifacts to be bundled into the managed solution as part of the solution build pipelines.

Tests

This extension also creates a Tests project under the plugins_srctests folder. It utilises Xunit and FakeXrmEasy to create tests for your plugins. The tests are run as part of the Azure DevOps build pipelines. The tests can be run locally by running the dotnet test command from the plugins_srctests folder.

The following is a sample test class:

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using FakeXrmEasy;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Xunit;

namespace PluginSRC.Tests
{
    public class PluginTest
    {
        // Write tests here

        // Sample code
        //
        [Fact]
        public void Test_contact()
        {
            var fakedContext = new XrmFakedContext { ProxyTypesAssembly = Assembly.GetExecutingAssembly() };
            var fakedService = fakedContext.GetOrganizationService();
            fakedContext.ProxyTypesAssembly = Assembly.GetExecutingAssembly();
            fakedContext.ProxyTypesAssembly = Assembly.GetAssembly(typeof(contact));

            var target = new contact { Id = Guid.NewGuid(), firstname = "bob" };
            fakedContext.Initialize(new List<Entity>{ target });
            ParameterCollection inputParameters = new ParameterCollection
            {
                { "Target", target }
            };
            fakedContext.ExecutePluginWith<CLASSNAME>(inputParameters,null,null,null);
            using (var systemServiceContext = new XrmSvc(fakedService))
            {
                contact returnedSession = systemServiceContext.contactSet.First();
                Assert.Equal("bob", returnedSession.firstname);
            }
        }
    }
}

See the FakeXrmEasy documentation for more information on how to use FakeXrmEasy.

Build Pipeline

Setup Connection String Variable

Configure a variable group to store the credentials for the Dataverse environment. To do this, navigate to the project settings and select Variable Groups under Pipelines. Select + Variable Group and give the group the name DevEnv.

Add a variable called ConnectionString with the value of the connection string for the Dataverse environment. Note: The connection string should be in the format:

AuthType=ClientSecret;Url=https://<orgname>.crm.dynamics.com;ClientId=<clientid>;ClientSecret=<clientsecret>;

Once the variable group has been created, configure the permissions (7) so that either the web resources pipeline or all pipelines have access to these variables.

Create DevOps Variables

Create Pipeline

Create the build pipleine from the templated azure-pipelines.yml file in the root of the repository. This should be the same process as show in the solutions page under "Create a pipeline".

Once the pipeline has been configured it will output the following artifacts:

Artifacts

The outputted dll file will be in release mode and ready for consumption by the solution build pipeline for release into upstream environments.

Test Results and Code Coverage

Tests and Code Coverage tabs will be available within DevOps after the build pipeline has run, see examples of the output below:

Test Results Code Coverage Code Coverage Detaild

Clone this wiki locally