Dynamics CRM: Simplify Development with Custom API for Reusability

Dynamics CRM introduced a preview feature (Custom API) for developers to make code reusability easier. For instance, the function that I used a lot is to take the current user’s Teams. I always have a requirement if the User is under team “A”, then some attributes need to be mandatory. Then in the Plugin also have a similar scenario If the user is under team “A”, needs to do a special operation. Here custom API will help a lot to make the Development much easier because we can get 1 function to handle Front-end and Back-end operations.

Plugin Code

To set up the Custom API, we need to prepare first the code that we want to use. Because the scenario not required any parameters (because CRM already has a mechanism to store the caller UserId), it will be more simple. Here is the query for getting the SystemUser.Teams based on SystemUserId:

using System;
using System.Linq;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
namespace SharedLibs
{
    public static class OrganizationServiceExtension
    {
        public static Entity[] GetSystemUser(this IOrganizationService service, Guid userId)
        {
            var query = new QueryExpression("systemuser")
            {
                ColumnSet = new ColumnSet("businessunitid"),
                NoLock = true
            };
            query.Criteria.AddCondition("systemuserid", ConditionOperator.Equal, userId);
            var teamMembershipLink = query.AddLink("teammembership", "systemuserid", "systemuserid");
            teamMembershipLink.EntityAlias = "a";
            teamMembershipLink.Columns = new ColumnSet(false);
            var teamLink = teamMembershipLink.AddLink("team", "teamid", "teamid");
            teamLink.EntityAlias = "b";
            teamLink.Columns = new ColumnSet("name");
            var result = service.RetrieveMultiple(query);
            return result.Entities.Any() ? result.Entities.ToArray() : new Entity[] { };
        }
    }
}

Then this is the EntityExtension to getting the AliasedValue (I make a blog post for TDD version of this in here):

using Microsoft.Xrm.Sdk;
using System.Linq;
namespace SharedLibs
{
    public static class EntityExtension
    {
        public static Entity GetAliasedEntity(this Entity entity, string alias)
        {
            var aliasEntity = new Entity();
            var attributes = entity.Attributes.Where(e => e.Key.Contains(alias)).ToArray();
            if (!attributes.Any()) return aliasEntity;
            foreach (var attribute in attributes)
            {
                var aliasedValued = attribute.Value as AliasedValue;
                if (aliasedValued == null) continue;
                if (string.IsNullOrEmpty(aliasEntity.LogicalName))
                {
                    aliasEntity.LogicalName = aliasedValued.EntityLogicalName;
                }
                
                aliasEntity[aliasedValued.AttributeLogicalName] = aliasedValued.Value;
            }
            return aliasEntity;
        }
    }
}

Here is the Plugin class that will use the upper functions:

using System;
using System.Linq;
using Microsoft.Xrm.Sdk;
using SharedLibs;
namespace Demo.Plugins
{
    public class DemoWebApi : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            var context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));
            var serviceFactory = (IOrganizationServiceFactory)
                serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            var service = serviceFactory.CreateOrganizationService(context.UserId);
            var users = service.GetSystemUser(context.UserId);
            var teams = users.Select(user =>
            {
                var team = EntityExtension.GetAliasedEntity(user, "b");
                return team.GetAttributeValue<string>("name");
            }).Distinct().ToArray();
            context.OutputParameters["Teams"] = teams;
        }
    }
}

You need to register this plugin (without plugin step) in your Dynamics CRM environment:

Register the plugin

Setup Custom API

First, on your PowerApps solution > New >Custom API :

Create Custom API

This is my set up for the Custom API:

Binding Type we set to Global because it isn’t tied to any Entity. Is Function needs to be Yes to make our Custom API being called in the HTTP method GET. Last, the Plugin Type we assigned to our Plugin that we create (without Plugin Step).

The next step is to make Custom API Response. Here are the settings:

Custom API response

You need to set the Name and the return Type the same as your code (I put the name as Teams and return type as string[]).

With this, our Custom API is already finished. You just need to Publish all customization before we test it.

Demonstration

To make sure, you can go to this link and find our Custom API Message:

<crm url>/api/data/v9.1/$metadata

This is the XML metadata:

Metadata XML

Because we set our code as GET, we can directly verify it in your browser. Just need to go to this URL:

<crm url>/api/data/v9.1/new_getcurrentuserteams

If you want to use Javascript. Here is the code for access it (for the time I post this, there is likely a bug in the Dynamics CRM WebApi. Therefore, I can’t use Xrm.WebApi.Online.execute method):

fetch('https://org6ac78613.crm5.dynamics.com/api/data/v9.1/new_getcurrentuserteams').then(response =>response.json()).then(json => console.log(json))

Here is the result:

Then here is the code for the Plugin to call our Custom API:

using Microsoft.Xrm.Sdk;
using System;
using System.Text;
namespace Demo.Plugins
{
    public class DemoPlugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            var context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));
            var serviceFactory = (IOrganizationServiceFactory)
                serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            var service = serviceFactory.CreateOrganizationService(context.UserId);
            var target = context.InputParameters["Target"] as Entity;
            if (target == null) return;
            var req = new OrganizationRequest
            {
                RequestName = "new_getcurrentuserteams"
            };
            var response = service.Execute(req);
            var teams = (string[])response.Results["Teams"];
            throw new InvalidPluginExecutionException("Test: " + string.Join(",", teams));
        }
    }
}

And here is the result on the plugin:

Happy coding folks!

Other Resources

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.