Microsoft Dataverse - Explore Custom API Feature In Dynamics 365 - Part Two

Introduction

Let me explain what we are going to do in our plugin code. We will pass Contact Record GUID as input parameter and then in plugin code we will use this record GUID to fetch required Contact entity details using Service Retrieve method.

Once we get the record we will return the data in JSON and set it in Output parameter which we will from Postman, Rest Builder and C# code.

Step  1

Create new class library project and add new class then copy and paste below code.

using System;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using XrmExtensions;

namespace Microsoft.Crm.Sdk.Samples.TestingCustomAPI
{
    [DataContract]
    public class CRMContact
    {
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string LastName { get; set; }
        [DataMember]
        public string EmailAddress { get; set; }
        [DataMember]
        public string MobilePhone { get; set; }
        [DataMember]
        public string Country { get; set; }
        [DataMember]
        public string ZIPCode { get; set; }

    }
    public class TestingCustomAPI : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            // Obtain the tracing service
            ITracingService tracingService =
            (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            // Obtain the execution context from the service provider.  
            IPluginExecutionContext context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));

            // Obtain the organization service reference.
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            if (context.MessageName.Equals("cr234_TestCustomAPI") && context.Stage.Equals(30))
            {
                try
                {
                    string dataRequest = string.Empty;
                    if (context.InputParameters.Contains("DataRequest") && context.InputParameters["DataRequest"] != null)
                    {
                        dataRequest = (string)context.InputParameters["DataRequest"];
                    }

                    if (!string.IsNullOrEmpty(dataRequest))
                    {
                        Entity contact = service.Retrieve("contact", Guid.Parse(dataRequest), new Xrm.Sdk.Query.ColumnSet(true));
                        if (contact != null)
                        {
                            using (MemoryStream SerializememoryStream = new MemoryStream())
                            {
                                //create a sample data of type Student Class add details
                                CRMContact crmContact = new CRMContact();
                                if (contact.Attributes.Contains(Contact.FirstName.ToString()))
                                {
                                    crmContact.FirstName = contact.Attributes[Contact.FirstName.ToString()].ToString();
                                }
                                if (contact.Attributes.Contains(Contact.LastName.ToString()))
                                {
                                    crmContact.LastName = contact.Attributes[Contact.LastName.ToString()].ToString();
                                }
                                if (contact.Attributes.Contains(Contact.PersonalEmail.ToString()))
                                {
                                    crmContact.EmailAddress = contact.Attributes[Contact.PersonalEmail.ToString()].ToString();
                                }
                                if (contact.Attributes.Contains(Contact.MobilePhone.ToString()))
                                {
                                    crmContact.MobilePhone = contact.Attributes[Contact.MobilePhone.ToString()].ToString();
                                }
                                if (contact.Attributes.Contains(Contact.Country_Region.ToString()))
                                {
                                    crmContact.Country = contact.Attributes[Contact.Country_Region.ToString()].ToString();
                                }
                                if (contact.Attributes.Contains(Contact.ZIP_PostalCode.ToString()))
                                {
                                    crmContact.ZIPCode = contact.Attributes[Contact.ZIP_PostalCode.ToString()].ToString();
                                }
                                // initialize DataContractJsonSerializer object and pass Student class type to it
                                DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(CRMContact));
                                //write newly created object(NewStudent) into memory stream
                                serializer.WriteObject(SerializememoryStream, crmContact);
                                SerializememoryStream.Position = 0;
                                //use stream reader to read serialized data from memory stream
                                StreamReader sr = new StreamReader(SerializememoryStream);

                                //get JSON data serialized in string format in string variable 
                                string Serializedresult = sr.ReadToEnd();
                                //Serialize the Entity Object and set it to Output parameter 
                                context.OutputParameters["DataResponse.TestCustomAPI"] = Serializedresult;
                            }
                        }
                    }
                }
                catch (FaultException<OrganizationServiceFault> ex)
                {
                    throw new InvalidPluginExecutionException("An error occurred in TestCustomAPI.", ex);
                }

                catch (Exception ex)
                {
                    tracingService.Trace("TestCustomAPI: {0}", ex.ToString());
                    throw;
                }
            }
            else if (context.MessageName.Equals("cr234_TestCustomAPI") && context.Stage.Equals(20))
            {
                try
                {
                    string dataRequest = string.Empty;
                    if (context.InputParameters.Contains("DataRequest") && context.InputParameters["DataRequest"] != null)
                    {
                        dataRequest = (string)context.InputParameters["DataRequest"];
                        context.InputParameters["DataRequest"] = "472aa6d8-c133-eb11-a813-000d3af020c4";
                    }
                }
                catch (FaultException<OrganizationServiceFault> ex)
                {
                    throw new InvalidPluginExecutionException("An error occurred in PreEventTestCustomAPI.", ex);
                }

                catch (Exception ex)
                {
                    tracingService.Trace("PreEventTestCustomAPI: {0}", ex.ToString());
                    throw;
                }
            }
            else
            {
                tracingService.Trace("TestCustomAPI plug-in is not associated with the expected message or is not registered for the main operation.");
            }
        }
    }
}

Key Points

  1. First we are checking Message name from context "context.InputParameters.Contains("DataRequest")"
  2. We are also checking if Stage/Plugin execution pipeline is Main Operation
  3. Extracting the input parameter value and converting it into GUID. We could also use GUID Data type as Request Parameter. I created it as String this is why I need to convert this to GUID.
  4. Now I am getting the required fields and forming the Json to set in output parameter

Step 2

Register your assembly and update Custom API record Plugin Type field with your class name which you have created in previous blog.

Step 3

Once you are done with the registration of Assembly. Open Rest Builder and find your custom API and execute by passing Contact record GUID in input parameter.

{Microsoft Dataverse} Explore Custom API feature in Dynamics 365

Step 4

We will execute our Custom API from C# code

ParameterCollection parameters = new ParameterCollection();
parameters.Add("DataRequest", "2902e1e5-5a43-eb11-a812-6045bd726160");
OrganizationRequest requestCustomApi = new OrganizationRequest() {
    RequestName = "cr234_TestCustomAPI",
        Parameters = parameters
};
OrganizationResponse responseCustomApi = crmSvc.Execute(requestCustomApi);
var stringProperty = (string) responseCustomApi.Results["DataResponse.TestCustomAPI"];

Output

{"Country":"India","EmailAddress":"[email protected]","FirstName":"Test","LastName":"Test","MobilePhone":null,"ZIPCode":"824230"}

Step 5

Test your Custom API from Postman

{Microsoft Dataverse} Explore Custom API feature in Dynamics 365

Custom API does support Pre-Event operation unlike Custom Action. Create new class file inside same class library and add below code.

if (context.MessageName.Equals("cr234_TestCustomAPI") && context.Stage.Equals(20)) {
    try {
        string dataRequest = string.Empty;
        if (context.InputParameters.Contains("DataRequest") && context.InputParameters["DataRequest"] != null) {
            dataRequest = (string) context.InputParameters["DataRequest"];
            context.InputParameters["DataRequest"] = "472aa6d8-c133-eb11-a813-000d3af020c4";
        }
    } catch (FaultException < OrganizationServiceFault > ex) {
        throw new InvalidPluginExecutionException("An error occurred in PreEventTestCustomAPI.", ex);
    } catch (Exception ex) {
        tracingService.Trace("PreEventTestCustomAPI: {0}", ex.ToString());
        throw;
    }
}

Key points

  1. First we are checking Message name from context "context.MessageName.Equals("cr234_TestCustomAPI")"
  2. Next we are checking in context stage is 20 which is Pre-Operation "context.Stage.Equals(20)"
  3. I am overriding the input parameter and changing the record GUID to different record GUID. It is hardcoded for testing purpose
  4. Make sure you deploy this code and register steps on Pre-Operation on Custom API message
  5. Now when you execute your Custom API, you will see contact details related to Pre-Operation contact record GUID

{Microsoft Dataverse} Explore Custom API feature in Dynamics 365

Custom API has an Execute Privilege Name (ExecutePrivilegeName) property which will let you to control your Custom API permission meaning you can only allow users to execute your Custom API who has certain privilege.

For example, I have updated my Custom API record to only allow users who has "prvCreateLead" access. Now when I execute this CUstom API with user who does not have Create Privilege on Lead entity he should get an error.

{Microsoft Dataverse} Explore Custom API feature in Dynamics 365

System.ServiceModel.FaultException`1: 'Principal user (Id=26adf271-1a02-eb11-a812-000d3af01a07, type=8, roleCount=2, privilegeCount=156, accessMode=0), is missing prvCreateLead privilege (Id=a41f965b-2619-4c73-8625-da31945cccc7) on OTC=4 for entity 'lead'.

Hope this helps!


Similar Articles