Skip to main content

Conditional branches

Tutorial 7 🔎

Source code

dependency injection - initial branch
conditional branches - final branch
conditional branches pull request - pull request with code changes

Overview​

In this tutorial we explain how conditional transitions can be organized in a process scheme. Furthermore, we describe a process example where different priorities are defined based on Actions that are executed as result of defining certain types of Conditions.

Conditions

The following stages are covered:

  1. Writing conditions in ActionProvider.
  2. Application build and run.
  3. Running the process.

Survey processing case​

In this case a quite simple logic for processing a survey is presented in the process scheme where the priorities for processing the queries of potential clients are calculated through defining several conditions.

Overall, a particular survey is received where the following information is identified:

  • Technology stack - it means which technology is implemented by the prospective client or company.
  • Amount of developers - how many developers work for the company.
  • Job title - the specific designation within an organization, normally associated with a job description.

First, the technology stack is validated. When it is different to .NET, the survey is just dismissed and the process is terminated. Otherwise, the process goes on.

Next, the amount of developers is verified. It enables to determine the size of the company: Small, Medium and Big.

Then, the job position is checked. Here, the job title is evaluated by considering the departments: Development, Management and Other.

note

The mechanisms for defining the described Conditions are specified in this section.

Once the Conditions are defined, the priorities for processing the surveys are calculated. These priorities will be set automatically as: High, Medium and Low.

Prerequisites​

  1. You should go through previous tutorials OR clone: dependency-injection branch.
  2. JetBrains Rider or Visual Studio.
  3. Command prompt or Terminal.

Backend​

The code improvements are described in this section.

ActionProvider​

The ActionProvider must be modified. Here, several changes are included to show how Conditions can be written in the application.

First, we have a standard implementation of _syncConditions. Besides, the realization of part of the methods IsConditionAsync and GetConditions which haven't been implemented previously. Furthermore, IsHighPriority and IsMediumPriority which define the synchronous conditions that were added.

When the condition named "IsHighPriority" is called, the WFE checks if this condition exists in the action provider. The GetConditions method is used for this check.

Next, the conditions are classified as asynchronous or synchronous. In this case, we will only consider synchronous conditions. The IsConditionAsync method is used for this check.

If a condition exists in the action provider, and it is synchronous, the ExecuteCondition method is called. This method finds the condition by name and executes it.

After that the conditions are defined as asynchronous or synchronous (in this case we consider only sync conditions). Next, the condition is passed to processInstance, and the name is returned through ExecuteCondition method.

Finally, either IsHighPriority or IsMediumPriority method will be executed. In these methods, the Report object is obtained from the process parameters, and the priorities are set based on the CompanySize and Position parameters provided earlier. As the order of condition checking is undefined, the function IsMediumPriority includes a call to the IsHighPriority function to make the workflow behavior predictable.

note

The object Report is filled out with data provided in previous steps of the process. How it works is described further in this section.

These new conditions will determinate the priorities for processing requests considering the example process presented in this tutorial. Below the resulting code.

Backend/WorkflowLib/ActionProvider.cs
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
using OptimaJet.Workflow.Core.Model;
using OptimaJet.Workflow.Core.Runtime;
using WorkflowApi.Hubs;

namespace WorkflowLib;

public class ActionProvider : IWorkflowActionProvider
{
private readonly Dictionary<string, Func<ProcessInstance, WorkflowRuntime, string, CancellationToken, Task>>
_asyncActions = new();

private readonly Dictionary<string, Func<ProcessInstance, WorkflowRuntime, string, bool>>
_syncConditions = new();

private IHubContext<ProcessConsoleHub> _processConsoleHub;

public ActionProvider(IHubContext<ProcessConsoleHub> processConsoleHub)
{
_processConsoleHub = processConsoleHub;
_asyncActions.Add(nameof(SendMessageToProcessConsoleAsync), SendMessageToProcessConsoleAsync);
_syncConditions.Add(nameof(IsHighPriority),IsHighPriority);
_syncConditions.Add(nameof(IsMediumPriority), IsMediumPriority);
}

public void ExecuteAction(string name, ProcessInstance processInstance, WorkflowRuntime runtime,
string actionParameter)
{
throw new NotImplementedException();
}

public async Task ExecuteActionAsync(string name, ProcessInstance processInstance, WorkflowRuntime runtime,
string actionParameter,
CancellationToken token)
{
if (!_asyncActions.ContainsKey(name))
{
throw new NotImplementedException($"Async Action with name {name} isn't implemented");
}

await _asyncActions[name].Invoke(processInstance, runtime, actionParameter, token);
}

public bool ExecuteCondition(string name, ProcessInstance processInstance, WorkflowRuntime runtime,
string actionParameter)
{
throw new NotImplementedException();

if (!_syncConditions.ContainsKey(name))
{
throw new NotImplementedException($"Sync Condition with name {name} isn't implemented");
}

return _syncConditions[name].Invoke(processInstance, runtime, actionParameter);
}

public Task<bool> ExecuteConditionAsync(string name, ProcessInstance processInstance, WorkflowRuntime runtime,
string actionParameter, CancellationToken token)
{
throw new NotImplementedException();
}

public bool IsActionAsync(string name, string schemeCode)
{
return _asyncActions.ContainsKey(name);
}

public bool IsConditionAsync(string name, string schemeCode)
{
throw new NotImplementedException();

return false; //we have no async conditions now
}

public List<string> GetActions(string schemeCode, NamesSearchType namesSearchType)
{
return _asyncActions.Keys.ToList();
}

public List<string> GetConditions(string schemeCode, NamesSearchType namesSearchType)
{
return new List<string>();

return _syncConditions.Keys.ToList();
}

//it is internal just to have possibility to use nameof()
internal async Task SendMessageToProcessConsoleAsync(ProcessInstance processInstance, WorkflowRuntime runtime,
string actionParameter, CancellationToken token)
{
await _processConsoleHub.Clients.All.SendAsync("ReceiveMessage", new
{
processId = processInstance.ProcessId,
message = actionParameter
}, cancellationToken: token);
}

private bool IsHighPriority(ProcessInstance processInstance, WorkflowRuntime runtime, string actionParameter)
{
dynamic report = processInstance.GetParameter<DynamicParameter>("Report");
return (report.CompanySize == "Big" && report.Position == "Management")
|| (report.CompanySize == "Small" && report.Position == "Development");
}

private bool IsMediumPriority(ProcessInstance processInstance, WorkflowRuntime runtime, string actionParameter)
{
dynamic report = processInstance.GetParameter<DynamicParameter>("Report");
return report.Position != "Other" && !IsHighPriority(processInstance, runtime, actionParameter);
}
}

Starting the application​

Now, the application can be run by executing the following commands:

cd docker-files
docker compose up --build --force-recreate

Then, open the application URL http://localhost:3000/ in the browser.

Upload the process scheme​

The ConditionalBranches.xml scheme provides a process example where different conditions will determinate the priorities for processing the received inquiries.

Process Scheme

Download the new ConditionalBranches.xml here.

Click on tab Designer to upload the process scheme as indicated below:

Upload scheme

Then, save the scheme.

Running the process​

In this section, we are going to describe how the process can be run.

Process triggering​

Initially, we have the Parameters that represent the results to certain survey. These simple three parameters are: technology stack, amount of developers and job title.

Parameters

Moreover, there are four methods for organizing conditional transitions:

  • First, the Decision activity. It presents a quite simple condition where the technology stack is checked. If it matches with our stack, then the process goes to next stage. Otherwise, the processing is terminated. The selected condition type is Expression which can be used by process parameters. This expression just takes the referred value: 'net' and validates if it is present.
Keep in mind

When method ToLower is called, the indicated @parameterName must be written in parentheses to avoid confusion with the syntax of partial parameters.

Detailed information regarding Expressions can be read in this documentation section.

Decision

  • Second, the Decision table activity. It enables to include several conditions in an activity and editing them in tabular form. Here, the size of the company is defined according to the following statements:

    • @How_many_developers_do_you_have_working > 0 AND @How_many_developers_do_you_have_working <= 20.

    • @How_many_developers_do_you_have_working > 20 AND @How_many_developers_do_you_have_working <= 100.

    • @How_many_developers_do_you_have_working > 100.

      These Expressions allow to classify the company as small, medium or big considering the amount of developers that work for it. Depending on the expression that is fulfilled, the process moves on.

Decision table

  • Once the company is classified, then the basic plugin activity SetParameter is used for Small Company, Medium Company and Big Company. It sets the activity Name, State, Parameter name and its Value. The Parameter name creates an object called Report where the field CompanySize will contain the values: "Small", "Medium" or "Big".

Basic plugin

  • Third, the Transitions that call IsManagement and IsDevelopment CodeActions. These actions check the value of the parameter of the process that contains information about the job description of the person who completed the survey.

Code Action

Code Action

  • These CodeActions are set in the transitions simply. Depending on the value that is valid, the transition is reached out, so there is not a specific order for calculating conditions.

Transition type

In other cases, the type of condition is set as Otherwise.

Transition type

  • Then, the SetParameter activities: Management, Development and Other are included. Here, the Parameter name creates the object Report with a field Position which will contain the values: "Management", "Development" or "Other".

Basic plugin

  • Fourth, the Transitions where IsHighPriority and IsMediumPriority methods are invoked. These methods define the conditions for calculating the priorities High, Medium or Low. In other cases, the condition will be set Otherwise. Further, a different criteria will be handled by Low Priority.

Transition type

Transition type

These Conditions are set in the ActionProvider as it was explained in this section.

Process execution​

Now, lets execute the process. Just click on button Create process to create a new process.

In this step you should simply indicate the technology stack, the amount of developers and the job title in the modal window 'Initial process parameters'. After that, click on blue button 'Create Process'.

Create process

Then, the inquiry will be automatically issued by considering the input data that have been provided.

Calculated priority

In addition, click on Designer toolbar and check Process info where the Report object will be saved and the issued data will be available.

Process info

Conclusion​

That's it! We have demonstrated in this tutorial four methods for organizing conditional transitions, and also we described how Conditions can be implemented in our WFE sample application.

info

Detailed information regarding Conditions in Workflow Engine can be read here.