username avatar
Kirti Kulkarni

Jun 14, 2022Beginner-12 min

5
12

Integrating Elsa .NET Workflows with ABP Commercial

ABP ELSA

In this article we have demonstrated about how to integrate .NET 5 Open-Source Workflow framework Elsa with ABP Commercial application.

What is Elsa .Net?

Elsa is an open-source .NET Standard Workflow framework. You can check it out at https://elsa-workflows.github.io/elsa-core/

Let us look at some of the key features of the Elsa .Net platform.

Elsa .Net is very Simple and Fast

The library is very compact and easy to use, at the same time, it is easy to extend through custom activities. Triggering event makes the corresponding workflow start automatically based on the event / recovery.

Elsa Supports long-running workflows

When the workflow is executed and needs to be done, for example, after user input, the workflow will stop, Keep, and run out of memory, until it can recover. It could be a few seconds later, a few minutes, few hours, days or even years.

Associate workflow with application-specific data

This is a key requirement of a long-running workflow. Store workflows in a file-based format, so we can make it a part of source control.

When we don't want the workflow to be part of source control, we can store it in a database based on the Web Version of the process designer. Whether it's storing workflow in a file system or in a database, whether to host the designer online or only on the local computer, need to be able to edit workflow online.

Use expressions to configure workflow activities

Usually, the information processed by workflow is dynamic in nature, Activities need a way to interact with that information. Workflow expressions support such scenarios.

Through application-specific activities, Support custom storage and script engine for an extension, call another workflow. This allows us to invoke reusable application logic from various workflow processes. Like from C# it's the same as calling general functions. View and analyze the workflow instances executed. View the path taken by the workflow, Runtime state, Where the failure occurs, and compensate for the failed workflow.

The workflow designer is embedded in our own dashboard application We can choose to create a workflow host that runs all the application logic , we can also choose to host the workflow runtime in a single microservice .

Separation of concerns

Workflow core library, the runtime and designer are completely separated in design. That is, when the workflow host should not rely on the web the designer. For example, This allows us to implement desktop-bas designers, or not use the designer at all, use YAML file. Last, Hosts only need workflow definitions and access to persistent storage. Support local or cloud management because Elsa It's just a set of NuGet software package.

Elsa with a separate, reusable HTML5 workflow designer web component, we can embed it in our own HTML on the web page. The designer runs entirely on the client-side and has a wealth of JavaScript API, The API allows us to define custom plug-ins to extend their functionality.

Getting Started with ABP commercial Angular application

Before creating project in abp.io we must purchase an active ABP commercial account

We are using Angular as the UI framework and Entity Framework Core as the database provider.

Setup our development environment

First things first! Let's setup our development environment before creating the first project.

Pre-requirements

The following tools should be installed on our development machine:

Redis (as the distributed cache) is required if we select the Public website option.

We can use another editor instead of Visual Studio if it supports .NET Core and ASP.NET Core.

Yarn v2 works differently and is not supported.

Install the ABP CLI

ABP CLI is a free & open-source tool for the ABP framework. It is also used for ABP Commercial application development.

First, we need to install the ABP CLI using the following command:

dotnet tool install -g Volo.Abp.Cli

If we’ve already installed, we can update it using the following command:

dotnet tool update -g Volo.Abp.Cli
Login to account

In order to use ABP Commercial features, we need to login our account using the ABP CLI

abp login 
Install the ABP Suite

ABP Suite is an application aims to assist us on our development.

First, we need to install the ABP Suite:

abp suite install

If we've already installed, we can update it:

abp suite update 
Abp Suite

Click to the Create a new solution button and fill the modal form:

Create New Solution
Using the ABP CLI to create a new project

Use the new command of the ABP CLI to create a new project:

Using Abp CliYour project successfully Created
The solution structures

The solution has a layered structure (based on the Domain Driven Design ) and contains unit & integration test projects. See the solution structure document to understand the solution structure in detail. https://docs.abp.io/en/commercial/latest/startup-templates/application/solution-structure

Create the Database
Connection String

Check the connection string in the appsettings.json file under the .HttpApi.Host project.

"ConnectionStrings": {
        "Default": "Server=localhost;Database=elsaangular;Trusted_Connection=True"
    }
Database Migrations

The solution uses the Entity Framework Core Code First Migrations. It comes with a .DbMigrator console application which applies the migrations and also seeds the initial data. It is useful on development as well as on production environment..DbMigrator project has its own appsettings.json. So, if we have changed the connection string above, we should also change this one.

The Initial Migration

DbMigrator application automatically creates the Initial migration on first run.

If we are using Visual Studio, we can skip to the Running the DbMigrator section. However, other IDEs (e.g. Rider) may have problems for the first run since it adds the initial migration and compiles the project. In this case, open a command line terminal in the folder of the .DbMigrator project and run the following command:

dotnet run

Running the DbMigrator

Right click to the .DbMigrator project and select Set as StartUp Project

Set As Startup Project

Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:

Started Database Migrations

Initial seed data creates the admin user in the database (with the password is 1q2w3E*) which is then used to login to the application. So, we need to use .DbMigrator at least once for a new database.

Run the Application
Running the HTTP API Host (Server Side)

Ensure that the HttpApi.Host project is the start-up project and run the application which will open a Swagger UI:Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If we don't have a debug purpose, this will be faster.

Book Store API
Running the Angular Application (Client Side)

Go to the angular folder, open a command line terminal, type the yarn command (we suggest to the yarn package manager while npm install will also work)Go to the angular folder, open a command line terminal, type the yarn command (we suggest to the yarn package manager while npm install will also work)

yarn

Once all node modules are loaded, execute yarn start (or npm start) command:

yarn start

It may take a longer time for the first build. Once it finishes, it opens the Angular UI in our default browser with the localhost:4200 address.

Login User
Elsa Integration
Backend
Installing Elsa Dashboard

The UI portion of Elsa Workflows is provided as a set of HTML web components in the form of the @elsa-workflows/elsa-workflow-designer NPM package.

The web components can be used as normal HTML tags in our own HTML documents, which means that we can re-host the designer and even the dashboard UI in our own web application.

To make it easy for ASP.NET Core developers to integrate the visual designer into their own project, the components are made available via the Elsa.Designer.Components.Web NuGet package, which embeds the necessary client asset files.

Add Package

To add the package to our ASP.NET Core project, we have to run the following command:

1. Host Project

dotnet add package Elsa

dotnet add package Elsa.Activities.Email

dotnet add package Elsa.Activities.Http

dotnet add package Elsa.Activities.Temporal.Quartz

dotnet add package Elsa.Persistence.EntityFramework.Sqlserver

dotnet add package Elsa.Server.Api

dotnet add package Elsa.Designer.Components.Web

2. pt-3 Domain Project

dotnet add package Elsa

dotnet add package Elsa.Activities.Email

dotnet add package Elsa.Activities.Http

dotnet add package Elsa.Activities.Temporal.Quartz

dotnet add package Elsa.Persistence.EntityFramework.Sqlserver

dotnet add package Elsa.Server.Api

After these installations we must do some changes in host projectOpen HttpApiHostModule.cs file We need to do some changes in the swagger method

Add the following line

private static void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
    {
     context.Services.AddAbpSwaggerGenWithOAuth(
     configuration["AuthServer:Authority"],
      new Dictionary<string, string>
       {
         {"elsaangular", "elsaangular API"}
       },
         options =>
       {
         options.SwaggerDoc("v1", new OpenApiInfo { Title = "elsaangular API", Version = "v1" });
         options.DocInclusionPredicate((docName, description) => true);
         options.CustomSchemaIds(type => type.FullName);
 
     //Add the below line
 
         options.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
       });
    }
Add a new function ConfigureElsa
private static void ConfigureElsa(ServiceConfigurationContext context, IConfiguration configuration)
    {
     context.Services
     .AddElsa(elsa => elsa
     .UseEntityFrameworkPersistence(ef =>
     DbContextOptionsBuilderExtensions.UseSqlServer(ef,
     context.Services.GetConfiguration().GetConnectionString("Default")))
      .AddHttpActivities()
      .AddQuartzTemporalActivities()
      .AddJavaScriptActivities()
      .AddEmailActivities()
      .AddWorkflowsFrom()
      .AddActivitiesFrom()
     );
 
     // Elsa API endpoints.
      context
      .Services
      //.AddWorkflowContextProvider() //use this when using custom context provide
      .AddElsaApiEndpoints();
      context.Services.Configure(options => { options.UseApiBehavior = false; }); //Add these lines 
      context.Services
      .AddCors(cors =>
      cors
      .AddDefaultPolicy(policy => policy
      .AllowAnyHeader()
      .AllowAnyMethod()
      .AllowAnyOrigin()
      .WithExposedHeaders("Content-Disposition")));//Add these lines 
    } 

Add these following lines inside ConfigureServices

public override void ConfigureServices(ServiceConfigurationContext context)
    {
     //We can disable this setting in production to avoid any potential security risks.
     Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
  
     var configuration = context.Services.GetConfiguration();
     var hostingEnvironment = context.Services.GetHostingEnvironment();
  
     ConfigureUrls(configuration);
     ConfigureBundles();
     ConfigureConventionalControllers();
     ConfigureAuthentication(context, configuration);
     ConfigureImpersonation(context, configuration);
     ConfigureSwagger(context, configuration);
     ConfigureElsa(context, configuration); // Add this line
     ConfigureLocalization();
     ConfigureVirtualFileSystem(context);
     ConfigureCors(context, configuration);
     ConfigureExternalProviders(context);
     ConfigureHealthChecks(context);
  
     // Add these two lines below
  
     PreConfigure(options => { options.UseHybridSerializer = false; }); 
     Configure(options => { options.AutoValidateFilter = type => type.Assembly != typeof(Elsa.Server.Api.Endpoints.WorkflowRegistry.Get).Assembly; }); line
  
    }

Add the following lines in OnApplicationInitialization function

public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
     var app = context.GetApplicationBuilder();
     var env = context.GetEnvironment();
  
     if (env.IsDevelopment())
      {
       app.UseDeveloperExceptionPage();
      }
  
     app.UseAbpRequestLocalization();
  
     if (!env.IsDevelopment())
      {
       app.UseErrorPage();
      }
  
     app.UseStaticFiles();
  
  
     if (MultiTenancyConsts.IsEnabled)
      {
       app.UseMultiTenancy();
      }
  
     app.UseUnitOfWork();
     app.UseIdentityServer();
     app.UseAuthorization();
     app.UseSwagger();
     app.UseAbpSwaggerUI(options =>
      {
       options.SwaggerEndpoint("/swagger/v1/swagger.json", "elsaangular API");
       var configuration = context.ServiceProvider.GetRequiredService();
       options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
       options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
      });
       app.UseAuditing();
       app.UseAbpSerilogEnrichers();
       app.UseHttpActivities();  /// Add this line here
       app.UseConfiguredEndpoints();
      }

Run the Backend

Use the command: dotnet run –launch-profile elsaangular.HttpApi.Host

Domain module

public override void ConfigureServices(ServiceConfigurationContext context)
    {
     PreConfigure(options => { options.UseHybridSerializer = false; }); // Add this line
   
     Configure(options =>
       {
         options.IsEnabled = MultiTenancyConsts.IsEnabled;
       });
   
     Configure(options =>
       {
         options.Languages.Add(new LanguageInfo("ar", "ar", "العربية", "ae"));
   
         options.Languages.Add(new LanguageInfo("es", "es", "Español", "es"));
       });
   
     #if DEBUG
         context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
     #endif
     },

In the HttpApi.Host Module add the following

<Project Sdk="Microsoft.NET.Sdk.Web">
    <Import Project="....common.props" />
    <PropertyGroup>
   <TargetFramework>net6.0</TargetFramework>
   <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
   <RootNamespace>elsaangular</RootNamespace>
     <PreserveCompilationReferences>true</PreserveCompilationReferences>
    </PropertyGroup>
    <ItemGroup>
   <PackageReference Include="AspNetCore.HealthChecks.UI" Version="6.0.1-rc2.4" />
   <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.1-rc2.4" />
   <PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="6.0.1-rc2.4" />
   <PackageReference Include="Elsa" Version="2.5.0" />
   <PackageReference Include="Elsa.Activities.Email" Version="2.5.0" />
   <PackageReference Include="Elsa.Activities.Http" Version="2.5.0" />
   <PackageReference Include="Elsa.Activities.Temporal.Quartz" Version="2.5.0" />
   <PackageReference Include="Elsa.Persistence.EntityFramework.SqlServer" Version="2.5.0" />
   <PackageReference Include="Elsa.Server.Api" Version="2.5.0" />
   <PackageReference Include="Elsa.Designer.Components.Web" Version="2.5.0" />
    
   <PackageReference Include="Serilog.AspNetCore" Version="4.0.0" />
   <!--Added Line-->
   <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
   <!--Added Line-->
   <PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="6.0.0" />
   <PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="6.0.0" />
   <PackageReference Include="Microsoft.AspNetCore.Authentication.Twitter" Version="6.0.0" />
   <PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="5.2.0" />
   <PackageReference Include="Volo.Abp.Autofac" Version="5.2.0" />
   <PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="5.2.0" />
   <PackageReference Include="Volo.Abp.Swashbuckle" Version="5.2.0" />
   <PackageReference Include="Volo.Abp.Account.Pro.Public.Web.IdentityServer" Version="5.2.0" />
   <PackageReference Include="Volo.Abp.Account.Pro.Public.Web.Impersonation" Version="5.2.0" />
   <PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Lepton" Version="5.2.0" />
    </ItemGroup>
    <ItemGroup>
   <ProjectReference Include="..elsaangular.Applicationelsaangular.Application.csproj" />
   <ProjectReference Include="..elsaangular.EntityFrameworkCoreelsaangular.EntityFrameworkCore.csproj" />
   <ProjectReference Include="..elsaangular.HttpApielsaangular.HttpApi.csproj" />
    </ItemGroup>
    <ItemGroup>
   <Compile Remove="Logs**" />
   <Content Remove="Logs**" />
   <EmbeddedResource Remove="Logs**" />
   <None Remove="Logs**" />
    </ItemGroup>
   </Project>
Angular Application

Create a folder named statichtml inside the assets folder then add an html file named as workflow-designer.html

Copy and paste the below contents inside this file.

<!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Elsa Workflows</title>
    <link rel="icon" type="image/png" sizes="32x32" href="{serviceurl}/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/images/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="{serviceurl}/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/images/favicon-16x16.png">
    <link rel="stylesheet" href="{serviceurl}/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/fonts/inter/inter.css">
    <link rel="stylesheet" href="{serviceurl}/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/elsa-workflows-studio.css">
    <script src="{serviceurl}/_content/Elsa.Designer.Components.Web/monaco-editor/min/vs/loader.js"></script>
    <script type="module" src="{serviceurl}/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/elsa-workflows-studio.esm.js"></script></head>
    <body className="h-screen" style="background-size: 30px 30px; background-image: url({serviceurl}/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/images/tile.png); background-color: #FBFBFB;">
    <elsa-studio-root server-url="{serviceurl}" monaco-lib-path="{serviceurl}/_content/Elsa.Designer.Components.Web/monaco-editor/min">
    <elsa-studio-dashboard></elsa-studio-dashboard>
    </elsa-studio-root>
    </body>
    </html>

Create a component workflow

ng g c workflow

cd workflow

ng g s workflow

Inside the workflow.service.ts

Add the function getDashboard()

getDashboard() {
        const url = window.location.origin + '/assets/statichtml/workflow-designer.html';
         const request = this.http.get(url, {
           headers: new HttpHeaders({
               Accept: 'text/plain'
           }), responseType: 'text'
         });
           
           return request;
       }   

Replace the contents in workflow.component.html by the following lines

<div id="workflow-dashboard-section"></div>

Replace the contents in workflow.component.ts by the following contents

 import { DOM_STRATEGY, CROSS_ORIGIN_STRATEGY, StyleLoadingStrategy, ScriptLoadingStrategy, LazyLoadService } from '@abp/ng.core';
       import { Component, OnInit } from '@angular/core';
       import { forkJoin } from 'rxjs';
       import { environment } from 'src/environments/environment';
       import { WorkflowService } from './workflow.service';
       
       const domStrategy = DOM_STRATEGY.AppendToHead();
       
       const crossOriginStrategy = CROSS_ORIGIN_STRATEGY.None(
       );
       
       const loadingStrategy1 = new StyleLoadingStrategy(
         environment.apis.default.url + '/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/images/favicon-32x32.png',
         domStrategy,
         crossOriginStrategy,
       );
       const loadingStrategy2 = new StyleLoadingStrategy(
         environment.apis.default.url + '/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/images/favicon-16x16.png',
         domStrategy,
         crossOriginStrategy,
       );
       const loadingStrategy3 = new StyleLoadingStrategy(
         environment.apis.default.url + '/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/fonts/inter/inter.css',
         domStrategy,
         crossOriginStrategy,
       );
       
       const loadingStrategy5 = new ScriptLoadingStrategy(
         environment.apis.default.url + '/_content/Elsa.Designer.Components.Web/monaco-editor/min/vs/loader.js',
         domStrategy,
         crossOriginStrategy,
       );
       const loadingStrategy6 = new ScriptLoadingStrategy(
         environment.apis.default.url + '/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/elsa-workflows-studio.esm.js',
         domStrategy,
         crossOriginStrategy,
       );
       
       @Component({
         selector: 'app-workflow',
         templateUrl: './workflow.component.html',
         styleUrls: ['./workflow.component.scss']
       })
       export class WorkflowComponent implements OnInit {
       
         private stylesLoaded$ = forkJoin([
       this.lazyLoad.load(
         loadingStrategy1,
       ),
       this.lazyLoad.load(
         loadingStrategy2,
       ),
       this.lazyLoad.load(
         loadingStrategy3,
       )
         ]
         );
       
         private scriptsLoaded$ = forkJoin(
       [this.lazyLoad.load(
         loadingStrategy5
       )]
         );
       
         scriptsAndStylesLoaded$ = forkJoin([this.stylesLoaded$, this.scriptsLoaded$]);
       
         constructor(private serviceWorkflow: WorkflowService, private lazyLoad: LazyLoadService) { }
       
         ngOnInit(): void {
       this.getDashboard();
         }
       
         getDashboard() {
       this.serviceWorkflow.getDashboard().subscribe(async res => {
         if (!res) { return; }
         this.scriptsAndStylesLoaded$.subscribe(element => {
       setTimeout(() => {
         const finalElsa = res.replace(/{serviceurl}/g, environment.apis.default.url);
         (document.querySelector('#workflow-dashboard-section')).innerHTML = finalElsa;
       }, 300);
         });
       });
         }
       
       }

Add these lines to main.ts file

 const script = document.createElement('script');
       script.src = environment.apis.default.url + '/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/elsa-workflows-studio.esm.js';
       script.type = 'module';
       document.head.appendChild(script);

elsa-workflows-studio.esm.js is called form the backend

Add the route to workflow dashboard in app-routing.module.ts

{
        path: 'workflow-dashboard',
        component: WorkflowComponent
    },

Open angular root folder. Run the Angular application

D:\elsaangular\angular - yarn start

DashboardCreate Workflow
A Simple Document Approval Workflow

Before we try out the workflow, let's setup an SMTP host. The easiest way to do so is by running Smtp4Dev using Docker:

docker run -p 3000:80 -p 2525:25 rnwood/smtp4dev:linux-amd64-3.1.0-ci0856

When Smtp4Dev has started, we'll be able to navigate to its dashboard at http://localhost:3000/ and inspect the emails the workflow will send.

Document Approval Workflow: Visual Designer

Creating the workflow visually is quite simple. Just add the activities one by one and configure their settings. However, there's one important difference between coded workflows and designed workflows: when designing visual workflows, there's (currently) no way to specify expressions using C# code as we did in the form of lambda statements in the DocumentApprovalWorkflow class. Instead, we must use JavaScript and Liquid expressions. The latter is specifically useful when we need to generate a body of text using dynamic data.

Without further ado, let's create a new workflow called using the dashboard!

Create Workflow

With the Elsa Dashboard in front of we, navigate to the Workflow Definitions page and click the Create button. We should now see an empty canvas with just a Start button and a cog wheel to configure workflow settings.

Let's do that first: click the cog wheel and specify the following:

  • Name: VisualDocumentApprovalWorkflow
  • Display Name: Visual Document Approval Workflow

Click Save.

HTTP Endpoint

Now click the Start button and look for the HTTP Endpoint activity and select it. Configure it with the following settings:

  • Path: /v2/documents
  • Methods: POST
  • Read Content: true (checked)
Set Document Variable

Click the Done outcome button on the previous activity and look for the Set Variable activity and configure it as follows:

  • Variable Name: Document
  • Value: input.Body (JavaScript)
Send Email to Josh the Approver

Click the Done outcome button on the previous activity and look for the Send Email activity and configure it as follows:

  • From: workflow@acme.com
  • To: josh@acme.com
  • Subject: Document received from Variables.Document.Author.Name (Liquid)
HTTP Response: Document Received

Click the Done outcome button on the previous activity and look for the HTTP Response activity and configure it as follows:

  • Status Code: OK
  • Content: Request for Approval Sent Our document has been received and will be reviewed shortly.
  • Content Type: text/html
Fork

Click the Done outcome button on the previous activity and look for the Fork activity and configure it as follows:

  • Branches: Approve, Reject, Remind
Approve: Signal Received

Click the Approve outcome button on the previous activity and look for the Signal Received activity and configure it as follows:

  • Signal: Approve
Send Approved Email to Author

Click the Done outcome button on the previous activity and look for the Send Email activity and configure it as follows:

  • From: workflow@acme.com
  • To: [Document.Author.Email] (JavaScript)
  • Subject: Document Variables.Document.Id Approved! (Liquid)
  • Body: Great job Variables.Document.Author.Name , that document is perfect. (Liquid)
Reject: Signal Received

Click the Reject outcome button on the Fork activity and look for the Signal Received activity and configure it as follows:

  • Signal: Reject
Send Rejected Email to Author

Click the Done outcome button on the previous activity and look for the Send Email activity and configure it as follows:

  • From: workflow@acme.com
  • To: [Document.Author.Email] (JavaScript)
  • Subject: Document Variables.Document.Id Rejected (Liquid)
  • Body: Nice try Variables.Document.Author.Name , but that document needs work. (Liquid)
Remind: Timer

Click the Remind outcome button on the Fork activity and look for the Timer activity and configure it as follows:

  • Timeout: Duration.FromSeconds(10) (JavaScript)
Send Reminder Email to Josh the Approver

Click the Remind outcome button on the Fork activity and look for the Timer activity and configure it as follows:

  • From: workflow@acme.com
  • To: josh@acme.com
  • Subject: Variables.Document.Author.Name is waiting for your review! (Liquid)

Now connect the Send Email activity we just added back to the Timer activity so that the workflow will keep sending reminders for as long as we haven't received a signal. To connect an outcome to an existing activity,f simply press and hold the SHIFT button while clicking the outcome. Release the SHIFT button and left click on the target activity:

signalImage
Join

Click the Done outcome button of the Send Email activity in the Approve branch and look for the Join activity and configure it as follows:

Mode: WaitAny

We also need to connect the Send Email Send Email activity from the Reject branch using the same technique used to connect the reminder Send Email activity back to the Timer activity:

Signal Received
HTTP Response: All set!

Finally, we just write a simple HTTP response, completing the workflow.

Click the Done outcome button of the Join activity and look for the HTTP Response activity and configure it as follows:

  • Status Code: OK
  • Content: Thanks for the hard work!
  • Content Type: text/html
Second Run

Make sure to publish our changes and then issue the following HTTP request:

POST /v2/documents HTTP/1.1
       Host: localhost: 44364
       Content-Type: application/json 
       {
           "Id": "4",
           "Author": {
           "Name": "Hannah",
           "Email": "hannah@gmail.com"
           },
           "Body": "This is another sample document."
       } 
Conclusion

Workflows are a powerful tool for implementing long-running process. Elsa aims to make this easy by providing an easy-to-use set of APIs and tools to design and execute workflows. Although we combined the Dashboard with workflow execution into the same application, this is not mandatory. We can see that the workflow is working as expected. Either we can call the URL from postman or any other REST API tools. It’s just as easy to create two separate applications, where one represents the dashboard, while the other represents your real-world application that executes the workflows designed with the Dashboard.

For more information, please check out https://elsa-workflows.github.io/elsa-core/.

Featured Comments
username avatar
Joe ThomsonToday at 5:42PM

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor

username avatar
Joe ThomsonToday at 5:42PM

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor

username avatar
Joe ThomsonToday at 5:42PM

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor

username avatar
Kirti Kulkarni

ABOUT THE AUTHOR

With over 20 years of experience in software development, Kirti heads Product R&D and Competency Management at WAi Technologies, leading the training and skills upgradation program at WAi. Kirti introduced the 'Women Back To Work' Initiative that encourages women to return back to mainstream software development after a career break or sabbatical.