User tasks

User assigned tasks are rather common in workflow based systems. They usually represent some need of either doing manual work by human actors or there is a need to collect some data from users. In anyway there is often the need to present this is human readable way (user interface) and to make sure people are aware of the tasks is to notify them.

Automatiko comes with following addons that help with user tasks within workflows

  • automatiko-user-tasks-management-addon

  • automatiko-user-tasks-email-addon

  • automatiko-user-tasks-slack-addon

  • automatiko-user-tasks-teams-addon

  • automatiko-user-tasks-index-fs-addon

  • automatiko-user-tasks-index-db-addon

  • automatiko-user-tasks-index-mongodb-addon

Regardless of type of notifications it can be disabled globally by setting (QUARKUS_AUTOMATIKO_NOTIFICATIONS_DISABLED=true) or on each user task via custom attribute called notification with value set to disabled

User tasks management

User task management addon brings basic support for displaying user focused information about tasks assigned to human actors. That is usually in form format where users can provide input required to continue with the workflow instance.

Automatiko does not aim at providing you with auto generated forms as this is usually more complex than automatic generation can handle. For instance layout of the form might be really complex, data that the form displays can come from various places and not all of them are kept in workflow instance etc.

Forms are based on templates that are suppose to be given as part of your service where Automatiko can locate them and present to the user when requested. All will be rendered as fully featured website so it can be easily embedded in custom websites for integration purpose.

Templates are based on Qute templating engine.

Developers can build their templates for each task to ensure that proper readability of the work to be done is given to users who get tasks assigned. Template files should be located in src/main/resources/templates folder of your service project. Files are expected to have following extensions

  • .html

  • .txt

It is recommended to use html as it is closer to the actual content of it.

Naming convention of the template files is as follows

  • task name property defined on the user task

  • name of the user task

  • workflow identifier with task name property defined on the user task

  • workflow identifier with name of the user task

For example when you have a task named My Task that has task name property approval within workflow with id 'vacations' then you can create following template files that will be found

  • approval.html

  • vacations.approval.html

  • My Task.html

  • vacations.My Task.html

Recommended it to use simple names that are defined as TaskName property of the user task as template name as it is also used as part of service api (REST api) so it will be easier to correlate these two.

Since these files are considered to be templates there are several variables given that will represent various data of the user task assigned.

Name Description

task

task instance in simplified format giving access to task name, description workflow metadata etc

link

link to the endpoint to work on a user task instance, allows to push data and complete it

inputs

input data of the user task

results

results data of the user task - this will be populated with inputs if the match by name and do not have value set

When there is no template file found for given task a default form will be returned but it will only provide simple text based form inputs. It is recommended to always provide custom templates for user tasks.

External templates loaded at runtime

Not always templates can be provided at build time, for instance when the existing ones should be customized. To be able to allow customization templates can be loaded at runtime based on folder path given by quarkus.automatiko.templates-folder configuration property.

All files with .html extension will be loaded and registered to the templating engine and by that can be used by user task forms and emails.

Configure

To enable user tasks management addon add following dependency to your service

<dependency>
  <groupId>io.automatiko.addons</groupId>
  <artifactId>automatiko-user-tasks-management-addon</artifactId>
</dependency>

Start process forms

As part of user task management addon, forms to start instances can also be provided. It works in exactly the same way as user tasks forms - based on template files. Templates must be named in following way:

  • {processid}.html where processid is the identifier used for process definition

  • {processid}_{version}.html where processid is the identifier used for process definition and version of the process definition if version is used

  • org/acme/test/{processid}.html where org/acme/test is the package name used for process definition transformed to a path (org.acme.test)

Using package names allows to have better structure of template files in case of any workflows exist in given project.

Templates can reference following parameters to provide more tailored forms

Name Description

id

unique identifier of the process definition

name

name of the process definition

description

optional description of the process definition

version

version of the process definition

url

url where the data should be send (posted) to start new instance

When the addon is used and form template is not defined for given workflow then a default form is returned. Default forms are for information only and do not allow to submit. Users can change the default templates providing template files with following names:

  • workflow-not-found.html - to provide a form when no dedicate template for workflow exists

  • workflow-not-authorized.html - to provide a form when user is not authorized to create instances of given workflow

Same parameters are available to default templates as described above, with one exception that url is not given for not authorized form.

User tasks email notifications

Human actors usually prefer to receive notifications when there is an awaiting task assigned to them. This is where email notifications come handy. Similar to forms, email notifications are template based but here relying on defaults might be actually a good thing.

Here is a sample email with default template for user task

email notification

Them main purpose for the notification is to give a hint there is something and provide base set of information.

Naming convention of the template files is as follows

  • task name property defined on the user task with suffix -email.html

  • name of the user task with suffix -email.html

  • workflow identifier with task name property defined on the user task with suffix -email.html

  • workflow identifier with name of the user task with suffix -email.html

For example when you have a task named My Task that has task name property approval within workflow with id 'vacations' then you can create following template files that will be found

  • approval-email.html

  • vacations.approval-email.html

  • My Task-email.html

  • vacations.My Task-email.html

There might be some special cases where extra information should be included but by default notifications via email aim at not exposing too much of the task context like input information.

Note that in case of multiple users are assigned to the task or assignment is based on group, all of these will receive an email It will be sent with dedicated link to the form so access to the task is really simple.

Since these files are considered to be templates there are several variables given that will represent various data of the user task assigned.

Name Description

name

name of the user task

description

description of the user task (can be null or empty)

taskId

unique identifier of the task instance

instanceId

unique identifier of the workflow instance task belongs to

processId

identifier of the workflow definition user task belongs to

processName

name of the workflow definition user task belongs to

inputs

current data set for the user task - in form of a Map

link

direct and absolute link to the form for user task

Customize email subject for notifications

Email subject that is sent for user task notifications is by default a fixed name with following value:

New task has been assigned to you (NAME OF THE TASK)

That’s not always desired and more tailored email subject is required. This can be achieved on each user task level by setting the EmailSubject data input to the value that should be used for actual notification email subject.

The email subject can also be generated based on data object via expressions.
quarkus.mailer.auth-methods=DIGEST-MD5 CRAM-SHA256 CRAM-SHA1 CRAM-MD5 PLAIN LOGIN
quarkus.mailer.from=YOUR_EMAIL@gmail.com
quarkus.mailer.host=smtp.gmail.com
quarkus.mailer.port=587
quarkus.mailer.start-tls=REQUIRED
quarkus.mailer.username=YOUR_EMAIL@gmail.com
quarkus.mailer.password=PASSWORD

In addition, service url must also be defined as it is used to construct the absolute url of the form sent out via email.

quarkus.automatiko.serviceUrl=https://myservice.hostname.com
When you run your service in development mode or test mode email are not being sent out so you can easily work on them without spamming too much. You can also use mock inbox to validate emails being sent out in your tests.

Email is sent only to valid email addresses so when your user ids are not represented as email address then you need to provide custom implementation of io.automatiko.engine.addons.usertasks.email.EmailAddressResolver interface that is responsible for resolving user and groups to their email addresses. You can also use that interface to suppress sending emails for certain users and/or groups.

User tasks slack notifications

Similar to email notifications, slack messages can also be used as notifications for user tasks. Slack integration is based on incoming webhooks feature in Slack and allows to send messages to given channel.

To be able to use slack integration, the channel (as name) needs to be specified on user task via custom attributes. The custom attribute is called slack-channel

The content of the message that will be posted to the Slack channel can be made user task specific as it is based on templates.

Naming convention of the template files is as follows

  • task name property defined on the user task with suffix -slack.txt

  • name of the user task with suffix -slack.txt

  • workflow identifier with task name property defined on the user task with suffix -slack.txt

  • workflow identifier with name of the user task with suffix -slack.txt

For example when you have a task named My Task that has task name property approval within workflow with id 'vacations' then you can create following template files that will be found

  • approval-slack.txt

  • vacations.approval-slack.txt

  • My Task-slack.txt

  • vacations.My Task-slack.txt

There might be some special cases where extra information should be included but by default notifications via slack aim at not exposing too much of the task context like input information.

Since these files are considered to be templates there are several variables given that will represent various data of the user task assigned.

Name Description

name

name of the user task

description

description of the user task (can be null or empty)

taskId

unique identifier of the task instance

instanceId

unique identifier of the workflow instance task belongs to

processId

identifier of the workflow definition user task belongs to

processName

name of the workflow definition user task belongs to

inputs

current data set for the user task - in form of a Map

link

direct and absolute link to the form for user task

Since the template for Slack is JSON based, the regular parts of the JSON structure { and } must be escaped in the template with \{ and \}.
Example of Slack template
\{
	"blocks": [
		\{
			"type": "section",
			"text": \{
				"type": "mrkdwn",
				"text": "Process '{processName}' requires user action on instance {instanceId}"
			\}
		\},
		\{
			"type": "section",
			"text": \{
				"type": "mrkdwn",
				"text": "A new task ({name}) has been assigned\n\n<{link}|View task details>"
			\}
		\}
	]
\}

Configure

To enable user tasks slack addon add following dependency to your service

<dependency>
  <groupId>io.automatiko.addons</groupId>
  <artifactId>automatiko-user-tasks-slack-addon</artifactId>
</dependency>

In addition to that there must be configuration of your slack channels webhook urls. Usually this is set in application.properties of your service but can also be given as system properties or environment variables

quarkus.automatiko.notifications.slack.test=https://hooks.slack.com......
quarkus.automatiko.notifications.slack.another=https://hooks.slack.com......
quarkus.automatiko.notifications.slack.onemore=https://hooks.slack.com......
There can be many channels defined and the names of the channels in application.properties file (e.g. test, another) are those that should be referenced in the user task custom attributes via slack-channel. This provides an abstraction layer on top of real channel names

User tasks teams notifications

Similar to slack notifications, Microsoft Teams messages can also be used as notifications for user tasks. Teams integration is based on incoming webhooks feature in MS Teams and allows to send messages to given channel.

To be able to use teams integration, the channel (as name) needs to be specified on user task via custom attributes. The custom attribute is called teams-channel

The content of the message that will be posted to the Teams channel can be made user task specific as it is based on templates.

Naming convention of the template files is as follows

  • task name property defined on the user task with suffix -teams.txt

  • name of the user task with suffix -teams.txt

  • workflow identifier with task name property defined on the user task with suffix -teams.txt

  • workflow identifier with name of the user task with suffix -teams.txt

For example when you have a task named My Task that has task name property approval within workflow with id 'vacations' then you can create following template files that will be found

  • approval-teams.txt

  • vacations.approval-teams.txt

  • My Task-teams.txt

  • vacations.My Task-teams.txt

There might be some special cases where extra information should be included but by default notifications via teams aim at not exposing too much of the task context like input information.

Since these files are considered to be templates there are several variables given that will represent various data of the user task assigned.

Name Description

name

name of the user task

description

description of the user task (can be null or empty)

taskId

unique identifier of the task instance

instanceId

unique identifier of the workflow instance task belongs to

processId

identifier of the workflow definition user task belongs to

processName

name of the workflow definition user task belongs to

inputs

current data set for the user task - in form of a Map

link

direct and absolute link to the form for user task

Since the template for MS Teams is JSON based, the regular parts of the JSON structure { and } must be escaped in the template with \{ and \}.
Example of MS Teams template
\{
  "@type": "MessageCard",
  "@context": "http://schema.org/extensions",
  "themeColor": "0076D7",
  "summary": "Task assigned - {name}",
  "sections": [
    \{
      "activityTitle": "Task assigned - {name}",
      "activitySubtitle": "{description}",
      "markdown": true
    \}
  ],
  "potentialAction": [
    \{
      "@type": "OpenUri",
      "name": "View task details",
      "targets": [
        \{
          "os": "default",
          "uri": "{link}"
        \}
      ]
    \}
  ]
\}

Configure

To enable user tasks teams addon add following dependency to your service

<dependency>
  <groupId>io.automatiko.addons</groupId>
  <artifactId>automatiko-user-tasks-teams-addon</artifactId>
</dependency>

In addition to that there must be configuration of your teams channels webhook urls. Usually this is set in application.properties of your service but can also be given as system properties or environment variables

quarkus.automatiko.notifications.teams.test=https://mycompany.webhook.office.com/webhook......
quarkus.automatiko.notifications.teams.another=https://mycompany.webhook.office.com/webhook......
quarkus.automatiko.notifications.teams.onemore=https://mycompany.webhook.office.com/webhook......
There can be many channels defined and the names of the channels in application.properties file (e.g. test, another) are those that should be referenced in the user task custom attributes via teams-channel. This provides an abstraction layer on top of real channel names

Customize templates

In cases where (email, slack, teams) templates have to customized at runtime they can be provided via application property that points to a folder where .html or .txt files will be loaded at startup and registered.

It can be given as environment variable QUARKUS_AUTOMATIKO_TEMPLATES_FOLDER=/templates or system property -Dquarkus.automatiko.templates.folder=/templates

Naming convention is the same as for creating templates as part of the source code.

Custom notification

Users can build custom notifications by implementing a io.automatiko.engine.addons.usertasks.notification.NotificationEmitter interface.

This interface is located in io.automatiko.addons:automatiko-user-tasks-notification

The implementation must be a CDI bean so it can be discovered and registered automatically.

User tasks index

User task index allows to collect all user tasks created within given service and expose it for queries. Most common use case is to find tasks assigned to given user, taking into consideration:

  • tasks assigned to users

  • tasks assigned to groups

  • tasks excluding users

In addition to that, task information can also be targeted as filter criteria.

Out of the box filter criteria are

  • name - name of the task, partial match

  • description - description of the task, partial match

  • state - state of the task (Ready, Completed, Aborted)

  • priority - priority of the task, partial match

Additional queries can be defined as custom that will provide more specific ways of searching for tasks. Such as taking into account other task properties or even inputs and outputs of the tasks. Though this is dependent on the type of storage used. This concept is known as CustomQueryBuilder and is described in details in each supported index addon.

Result can also be sorted either ascending or descending by task fields.

Once addon is added to the service, it will expose additional REST endpoint under /index/usertasks with following methods:

  • find tasks by filter criteria

  • find task by id

  • query with custom queries

User tasks are kept in the index according to instance end configuration. Instance end property can be set to

  • remove - (default) that removes process instances upon its completion

  • keep - to keep instances in the storage after they are completed

  • archive - to archive process instances upon their completion

Index will be default remove tasks that are completed to keep the index size manageable. But if the instance end configuration is set to keep then tasks will be kept in the index even when they are completed. That allows to search for tasks that were already completed.

Tasks are automatically indexed after addon activation. Each task created within the service will be added to the index and by that can be searched for.

User task index is only supported with file system, db and mongodb persistence

Index for file system persistence

File system persistence utilizes CQEngine for efficient indexing and searching. This allows to collected and efficiently search big volumes of tasks.

Custom query builder

Custom query builder in file system index addon requires two things to be done:

  1. register additional indexes on non yet indexed fields

  2. define where clause based on incoming parameters

Each custom query builder must implement methods of io.automatiko.addons.usertasks.index.fs.CQEngineCustomQueryBuilder

Following is an example building a custom query to filter out by process id.

import static com.googlecode.cqengine.query.QueryFactory.in;

import java.util.List;
import java.util.Map;

import com.googlecode.cqengine.attribute.SimpleAttribute;
import com.googlecode.cqengine.index.suffix.SuffixTreeIndex;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.query.option.QueryOptions;

import io.automatiko.addons.usertasks.index.fs.CQEngineBasedIndex;
import io.automatiko.addons.usertasks.index.fs.CQEngineCustomQueryBuilder;
import io.automatiko.addons.usertasks.index.fs.CQEngineUserTaskInfo;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped(1)
public class ByProcessCustomQueryBuilder extends CQEngineCustomQueryBuilder {

    public static final SimpleAttribute<CQEngineUserTaskInfo, String> PROCESS_ID = new SimpleAttribute<>("processId") {(2)
        public String getValue(CQEngineUserTaskInfo task, QueryOptions queryOptions) {
            return task.getProcessId();
        }
    };

    public ByProcessCustomQueryBuilder() {
    }

    @Inject
    public ByProcessCustomQueryBuilder(CQEngineBasedIndex index) {
        index.get().addIndex(SuffixTreeIndex.onAttribute(PROCESS_ID));(3)
    }

    @Override
    public Query<CQEngineUserTaskInfo> build(Map<String, List<String>> parameters) {

        return in(PROCESS_ID, parameters.get("pid"));(4)
    }

    @Override
    public String id() {
        return "byprocess";(5)
    }

}
1 Make the class a CDI bean so it can be discovered
2 Create attribute definition for CQEngine index for given field
3 Register new index for PROCESS_ID field
4 build CQEngine where clause
5 Assign unique id for the query builder

Configure

To enable user tasks fs index addon add following dependency to your service

<dependency>
  <groupId>io.automatiko.addons</groupId>
  <artifactId>automatiko-user-tasks-index-fs-addon</artifactId>
</dependency>

In addition to that there must be path provided where the index should be stored. Usually this is set in application.properties of your service but can also be given as system properties or environment variables

quarkus.automatiko.index.usertasks.fs.path=/data

The path should point to directory that is writable and upon start a file automatiko-user-tasks.dat will be created.

Index for data base persistence

Data base index addon creates new table (USER_TASK_INDEX) that will keep all user tasks of the service. This table is then queries to get tasks for given user.

Custom query builder

Custom query builder in db index addon requires to create a instance of io.automatiko.addons.usertasks.index.db.DbQueryFilter that provides a JPQL where clause and set of values for bind parameters.

Each custom query builder must implement methods of io.automatiko.addons.usertasks.index.db.DbCustomQueryBuilder

Following is an example building a custom query to filter out by process id.

import java.util.List;
import java.util.Map;

import io.automatiko.addons.usertasks.index.db.DbCustomQueryBuilder;
import io.automatiko.addons.usertasks.index.db.DbQueryFilter;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped (1)
public class ByProcessIdDbCustomQueryBuilder extends DbCustomQueryBuilder {

    @Override
    public DbQueryFilter build(Map<String, List<String>> parameters) {

        return new DbQueryFilter("t.processId = :process", Map.of("process", parameters.get("process").get(0))); (2)
    }

    @Override
    public String id() {
        return "byProcess"; (3)
    }

}
1 Make the class a CDI bean so it can be discovered
2 Create where clause based on parameters
3 Assign unique id for the query builder

Configure

To enable user tasks db index addon add following dependency to your service

<dependency>
  <groupId>io.automatiko.addons</groupId>
  <artifactId>automatiko-user-tasks-index-db-addon</artifactId>
</dependency>

Index for MongoDB persistence

MongoDB index addon creates new collection usertasks in the same database as the service. This collection is the queries to find tasks for given user and filter criteria.

Custom query builder

Custom query builder in mongodb index addon requires to create Bson filters according to MongoDB requirements.

Each custom query builder must implement methods of io.automatiko.addons.usertasks.index.mongo.MongoDBCustomQueryBuilder

Following is an example building a custom query to filter out by process id.

import java.util.List;
import java.util.Map;

import org.bson.conversions.Bson;

import com.mongodb.client.model.Filters;

import io.automatiko.addons.usertasks.index.mongo.MongoDBCustomQueryBuilder;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped (1)
public class OrderAmountQuery extends MongoDBCustomQueryBuilder {

    @Override
    public Bson build(Map<String, List<String>> parameters) {
        Bson filter = Filters.and(Filters.eq("processId", "orderItems"),
                Filters.gt("inputs.order.total", Double.parseDouble(parameters.get("amount").get(0)))); (2)
        return filter;
    }

    @Override
    public String id() {
        return "orderAmount"; (3)
    }

}
1 Make the class a CDI bean so it can be discovered
2 Create where clause based on parameters as Bson
3 Assign unique id for the query builder

Configure

To enable user tasks db index addon add following dependency to your service

<dependency>
  <groupId>io.automatiko.addons</groupId>
  <artifactId>automatiko-user-tasks-index-mongodb-addon</artifactId>
</dependency>

User task index management

When user tasks index addon on is configured it will also provide a management API. API is available under /management/index/tasks endpoint. Main usage of this api is to perform initial reindex after index addon was introduced or to resolve any out of sync instances.

This API is by default hidden in OpenAPI and has following capabilities:

  • reindex all processes within the service

  • reindex instances of a given process (by id)

  • reindex only a single process instance of a given process (by id)