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.
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
whereprocessid
is the identifier used for process definition -
{processid}_{version}.html
whereprocessid
is the identifier used for process definition andversion
of the process definition if version is used -
org/acme/test/{processid}.html
whereorg/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
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 \} .
|
\{
"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 \} .
|
\{
"@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:
-
register additional indexes on non yet indexed fields
-
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 |
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 |
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)