Showing posts with label workflow. Show all posts
Showing posts with label workflow. Show all posts

Nov 17, 2023

Write a Custom API call from Clout Flow

In a previous article, we check how we Write a Custom API and calling it from classic workflow. It worked well, yet we encountered some hiccups in deployment. Actually, recommended way is to call it from a Cloud Flow than a workflow.

Lets check that using a simple sample Cloud Flow. Implementation of the Custom API remains the same. Please refer that part from previous post. 

Lets do the calling part from a Cloud Flow. I would select a flow that triggered on demand from the custom entity (in our case so_office).

First I am selecting a task to trigger when selecting a record from Office where I need to select the environment along with entity.


Then I need to select Perform a bound action from list of actions of Microsoft Dataverse.


Now interestingly, we can see our Custom API within the Action lists shown, which we would select as below. Then we need to pass the row Id passed by trigger from the first action.


Now if you go open and office record, you are able to runt he Flow on Demand as any other workflow is triggered. 


Here I demonstrated only simple on demand flow for ease of demonstration, but what we need to learn is, in any flow we can call out Custom API using Performa Bound Action of Microsoft Dataverse. Most importantly, this is the recommended way than calling from a workflow. 

Oct 18, 2023

Write a Custom API and call from Workflow

A Custom API is comparatively a new mechanism to create your own API in Dataverse. This is like a Action where you can package few steps to accomplish something. Advantage is you can then call from anywhere, either client side or server side etc. Obviously it can be called via a workflow or Cloud Flow too. Custom API is more flexible since coding is possible.

In this scenario we will see how to create a simple Custom API and call it from a classic workflow.

Writing a Custom API

1) We need to write a code which is pretty similar to a Plug-in. This is where we define our logic. In my case I am accepting a parameter string (i.e. DetailStr). Logic is to update Description (i.e. so_description) field of the custom entity called Office (i.e. so_office) with parameter value.

namespace AccAdmin.Plugins.CustomApi
{
    public class UpdateOffice : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context;
            IOrganizationServiceFactory factory;
            IOrganizationService service;

            try
            {
                context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
                ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

                if (!context.InputParameters.Contains("DetailStr"))
                    throw new InvalidPluginExecutionException($"DetailStr not designated or Invalid..");

                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is EntityReference target)
                {
                    if (target.LogicalName != "so_office") return;
                    factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                    service = factory.CreateOrganizationService(context.UserId);

                    Entity entity = new Entity();
                    entity.LogicalName = "so_office";
                    entity.Id = context.PrimaryEntityId;
                    entity["so_description"] = (string)context.InputParameters["DetailStr"];
                    service.Update(entity);
                }
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                throw e;
            }
            finally
            {
                service = null;
                factory = null;
                context = null;
            }
        }
    }
}

2) Once register the Plug-in, Custom API step will be visible as a step as below.


3) Now we start configuration. First add new Custom API to your solution.
    

Once save my Custom API was seen like below. It will have an unique name which we use to call this. We have set Enable for Workflow = Yes since we plan to call this from a Workflow. Most importantly, we have set our Plug-in step as the Plugin Type which is the linking point of our logic to the API. 


* Idea of all other settings

4) Same way we added the Custom API, we now add a parameter to our solution. Parameter is optional. In our case we plan to pass a string parameter.


Once created, parameter was seen as below. Nothing complex about this. You may notice we have selected Custom API we defined in previous step is set here for Custom API field.


Now we are done with our Custom API

Calling the Custom API from Workflow

Here I am create just an On Demand workflow so we can easily check it.

5) I am initiating a very simple Async workflow against Office entity.


6) Now I am adding a step of performing an Action. Interestingly, when I check the list of available Actions, I am seeing my Custom API by its unique name.


I see two parameters here. One is Target where we need to pass the current office record. Other one is what we define in Custom API which is needed for our logic.


7) Now its a matter of running our Workflow On Demand for any selected Office record.

* 
Unique Name: Unique name for the Custom API (Start with the publisher’s prefix which you have specified on your solution).

Name: Name for the Custom API.

Display Name: Display Name for this Custom API. In case we have enabled multi-languages on the System, we can provide a specific name for the custom API based on the language.

Description: This field is to store the description of this Custom API.

Binding Type: The Binding Type is the Options set field and could be set as Global, Entity, and Entity Collection. This should be specified as per your operation requirement (Entity Collection is only supported in Function Type Custom API).

Bound Entity Logical Name: This field is required when we select the Binding Type as Entity or Entity Collection. For the binding type as Global, it can be empty.

Is Function: It defines whether your custom API is a Function (this can only be called using Get Method) or Action (this can only be called using POST method).

Is Private: To define the Custom API is private or public (setting it private makes it accessible to only the owner of the Custom API).

Allowed Custom Processing Step Type: Allowed Custom Processing Step Type is another Option set field with options like None, Async Only, Sync, and Async. These options let you define whether the other plugins could be registered on this Custom Message and also lets you define its behavior. Like Async Only allows the plugins to register only onPost operation with Execution Mode to Asynchronous, and same for the Sync and Async option.

Execute Privilege Name: We can define the privilege that allows execution of the custom API. As per Microsoft docs, we can also create custom privileges and it is currently in development. We can use OOB privileges, for example,prvCreateLead, prvWriteLead, etc.

Plugin Type: Set the reference of your plugin for this API.

Warning
This example works fine. Anyway, there has been some deployment issues reported as per now we write this. So it is always advisable to call your Custom API from a Cloud Flow than a classic workflow. That worked perfectly.

May 4, 2023

Workflow to run against BPF entity

Though we do so many Business Process Flows, it is not that common to trigger another workflow when applying the BPF. Well ,it is possible. As we do in other instances, we only have to select the BPF Entity/ Table and you will see below configuration page which is pretty usual.

If look carefully, you will notice quite different trigger events are lined-up. In given example, we have selected Process is Applied and Process Status changes.


One of the cool things you can do is to set active step depending on some conditions (Conditions in the entity record where BPF is defined based on). For example, if the BPF is done on Account entity, we can control which step BPF should jump into based on some fields of Account. 

Caution

Anyway, there is one situation you have to be careful of. In some cases, we write more than one Business Process Flows and create a switching mechanism in JS Script. So record is jumping to correct BPF upon opening of the record.

In this case, if you write a WF against a BPF which is not the default one, pl keep in mind your Workflow which should have triggered for Process is Applied will not trigger immediately. It will trigger only after someone open the record. Obviously, JS will fire only after record is open and then only correct BPF is applied for the first time. Make sense right ?

Aug 16, 2022

Retrieve instance URL from Custom Workflow Activity

When this requirement arises, I though it should be a matter of reading it from context or so on. Anyway, then I realized it’s not available and no straightforward way of doing it. So I would suggest below two ways to do that based on your circumstance. 

If your system has a separate entity for configuration data, like key value pairs, its best to store there. Advantage is this entry could be accessed from many other areas as needed. Since its store as data, deployments don’t override. 

If you really want to retrieve dynamically, there is one other way.

IWorkflowContext context = ExecutionContext.GetExtension<IWorkflowContext>();
context.OrganizationName

This attribute gives you the unique name of the instance. 

While this is unique to instance, you are able to write a case statement etc. to retrieve the correct URL. This is good because your system will switch dynamically to correct URL but you are keeping URLs in the code itself. This means if you add new environment you need to modify the code and re-deploy the assembly.

Aug 9, 2022

How to track the changes done for a Workflow

There are two parts to the question. If you have written Custom workflow Activities, obviously you have written in Visual Studio (Or similar Dev tool) and may have stored in a repository. In fact, you are all good to track back what changes are made by developers in different occasions. 

Tricky part comes next: How to track down the changes done to a Workflow through the OOB workflow template. That’s adding/ removing different actions such as Create, Update etc. Unfortunately, there is no proper method. 

Anyway, there is a little trick. If Workflow was commissioned to the system for a while (Async or real time), chances are there could be sessions those failed. Failed sessions are always kept associated to the workflow. 

Now if you check an old failed session, you will be able to see all the steps existed in the workflow at that time. This is not a perfect solution, but I managed to explain an issue in one of my projects today by doing this to prove someone has removed an essential step. Unless this method, no one would be able to explain.

Thought of sharing since this could help someone!

Apr 25, 2021

Something to remember when executing On Demand workflows

We sometimes convert existing workflows to On Demand to get something done. This is a normal practice and nothing wrong with it. Yet there is one thing to know before doing this.

Consider your workflow is set to execute under the context of Owner of the Workflow. Idea behind is user who is doing the operation may not have permissions to do what workflows is doing. So workflow is set to have much higher/ elevated user to do that. 

Catch here is, when workflow is running as On Demand, it will always run within the context of the user who is performing the operation irrelevant of the setting. You will see some sessions are listed under the user who is running the workflow On Demand as below.

Sep 1, 2019

Is Workflow a thing of past ?

When you start working with Dynamics 365 CE Unified Interface, one of the things you would notice is there is no menu item to see available On Demand Workflows against a record.

This happens because this option is set to No by default. Below is the place (Settings > Administration > System Settings > Customization tab) to switch it Yes.


Once this is switched to Yes, new menu item will be visible called Flow (not Workflows!) as below, that contains relevant On Demand workflows. 


By the way, why this menu area called Flow? Well that's interesting. Flow is going to be the successor of good old Workflows! That's what Microsoft is recommending. As a evidence, when try to create a new workflow, you can now see it is pushing the user to move to Flow as below;


Is Workflow a thing of past ?

Dec 8, 2016

Passing and Receiving values (parameters) in Custom workflow

Just thought of posting this code snippet on input/output parameters in CWF. In most of the places it explains well on how to declare the parameters, but not using of get, set methods in the code.

Here how we declare them;

[RequiredArgument]
[Input("Salary")]
public InArgument<Double> SalaryVal { get; set; }

[OutputAttribute("Tax")]
public OutArgument<Double> TaxVal { get; set; }

...and this is how you associate to the code within the Execute method;

protected override void Execute(CodeActivityContext executionContext)
{
//...........
//...........

Double _salaryVal = SalaryVal.Get(executionContext);

// Calculation goes here
// and return to _taxVal

TaxVal.Set(executionContext, _taxVal);

}

Jul 14, 2014

Sample custom workflow

We will see how you do a custom workflow. We all know how to do things with a normal workflow. Still in some instances, built in wizard of workflows are not able to cater our need. In that case we need to do a code to be executed as a part of the workflow.  

Pl check below code. This shows you a skeleton of a workflow code. If you notice, what we really do is reading some attributes from the context and do whatever we need to do. In fact, idea is quite similar to construction of a plug-ins.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Activities;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Workflow;

namespace CustomWFtest
{
 public class SetAccountOffice : CodeActivity
 {
  protected override void Execute(CodeActivityContext executionContext)
  {
    IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

    if (context == null)
    {
        throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
    }

    IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
    IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
    OrgOrionDatamodel orgSvcContext = new OrgOrionDatamodel(service);

    try
    {
        throw new Exception("testing exception .. primary ID is : " + context.PrimaryEntityId);
       
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        context = null;
    }
  }
 }
}

Context gives you some attributes from the triggering point of the workflow. Our sample just throw an exception with primary Id. Use the intellisense to check what other attributes we got in the context and decide how you go with it.


Check below illustration to know what other references we use in this class library project (same as plug-in). Microsoft.Xrm.sdk.workflow dll is the new one we need to add. As same as plug-in development, you must sign the assembly (right click the project > properties > Signing > create .snk file).


Once you do the code, build the dll and register through the plug-in registration tool.


Then you can start workflow wizard to do the needful.  Now, within the wizard, you can see our newly registered DLL with custom workflow name as under “Add Step”. Now you can easily add it as a step.


You can see, in our sample, the exception with primary ID of the record. (go to system jobs and check)

If you wisely study you may realize context we used within the code contains the data relevant to this triggering entity and etc. Hope you understand “context” is the link in between the workflow and your code. Once you understand this, sky is the limit for actions you can do within the code.

Nov 10, 2013

Workflow that waits till a date in the record

This is something cool. If I elaborate this a bit; this will allow you to trigger something on an important day like contract end date or etc. This can actually replace the need of a windows service that checks something periodically.

Of course I wrote something similar sometimes back, but it is practically bad. It could make new instance again and again till the exact date reaches.

Now it is just one line waiting statement that does the magic.

Typical example I tried is to put a flag for insurance records when they reach expiry date.
 
In CRM 2011, it will be like this;
 
 
In CRM 2011 online it will be like this;

 
In CRM 2013 online
 
 
If you check the record after creation you will see the waiting statement in the workflow tab.
 
 
Since expiry date can be changed later on, it is also advisable to trigger on Update as well as on Create. If a user changes the expiry date later on, you will see an extra waiting record.
 
 
Anyway, as I tested, it worked for the correct expiry date. 


Nov 21, 2012

Recursive workflows instead of windows services?

Please refer this for better solution.

In Dynamics CRM, we don’t have a facility to schedule an activity. Typically, one might need to change the status of a record without any interaction/ trigger. For example expiration of insurance policy. For this what we usually do is writing a windows service.

http://crmbusiness.wordpress.com/2011/05/24/crm-2011-how-to-schedule-recurring-workflows-in-crm-2011/

This blog article encourages us to use a recursive workflow instead of a windows service. It is a clever way of writing a workflow in a recursive pattern with delays to accomplish the objective. It seems fantastic since it can be achieved without any trouble or coding. I did a test workflow as this author explains (with a waiting time of 1 hour) and it worked well for me. 

Anyway, it is yet to be taken to the discussion table to check positive and negative consequences.

Also I think, it is better to do the state change like simple operation through the workflow and triggers a plug-in for rest of the complex operations if the requirement consists of lengthy logics.
Furthermore, we can even identify this call by filtering by asynchronous call filter as below.

if (context.CallerOrigin.GetType() == 
            typeof(Microsoft.Crm.Sdk.AsyncServiceOrigin))
        {
            //plug-in steps
        }