Workflow

In explaining workflow, let's first take a look at the following things:

  • Workflow Models
  • Workflow Instances
  • Workflow Tasks
  • Workflow Payload Resources
  • Workflow Comments
  • Workflow History Item
  • Workflow Events
  • Workflow Event Handlers

A workflow model is a definition of a workflow consisting of a full set of instructions on how content should be
routed between participants or activities on its way toward completion. A workflow model is kind of like a blueprint
describing the sequence of steps to be taken.

A workflow instance is a running workflow that has been started. Each instance runs a workflow model's instructions
from start to end. A workflow instance maintains properties (data), attached payload resources (content)
and other lifecycle state.

A workflow payload resource is a piece of content (a content node) that has been attached to a workflow instance.
Every workflow instance can have zero or more payload resources. You can also start workflows that don't have any
payload resources if you want to.

A workflow task is an assignment that is created when a workflow transitions from one state to the next. The task
holds point-in-time information about an activity to be completed. Each task holds its own copy of the workflow's
properties (data) and attached payload resources (content) so that it can be worked on in isolation. When a task
completes, the data and content are merged back into the workflow instance. And then the workflow continues on.

A workflow comment can be created at any time and is attached to both a workflow task and a workflow instance. This
lets you see comments made for a specific task or comments made across the entire workflow.

A workflow history item is an automatically managed object that gets created in the background every time someone
interacts with a workflow. It supplies a record of every change to the workflow task and workflow instance, letting
you see a perfect history of when people modified properties (data), updated payload resources (content), added
comments, fired actions or transitioned between workflow tasks.

A workflow event is a lifecycle event that is raised as the workflow starts, ends or moves from task to task. There
are a number of potential events that are raised and each workflow model has the opportunity to render one or more
workflow handlers to optionally handle these events.

A workflow event handler is an event listener that hears when a workflow event is raised and then does something.
These are a number of workflow event handlers available out-of-the-box including handlers for web hook callouts (over
HTTP), email and Cloud CMS actions. You configure workflow event handlers with a little bit of JSON.

Workflow Models

A workflow model is a JSON document that defines a workflow. In a sense, a workflow model is a full set of instructions
about how a process should be routed on its way toward completion. A workflow model consists of the following:

  • Nodes
  • Transitions
  • Swimlanes
  • Forms

Nodes and Transitions

A workflow model is foremost defined by a graph that contains a start node and an end node.
The graph defines a flow that the process takes. The flow starts at the start node and ultimately must
end up at the end node. You can set up any structure you'd like between the start and end, including
conditional flow paths, parallel and serial flow paths and forks and joins.

Let's take a look at a really simple example:

{
    "title": "Approve an article #1",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "initiator"
        },
        "end": {
            "type": "end"
        }
    }
}

This example defines a one-hop approval process for an article.

This example workflow has three nodes (start, node1 and end).
Every workflow has a start node and an end node. The workflow defines a transition
from the start node to the node1 node named start.
This transition gets fired automatically when the workflow starts. Technically, it can be named anything. The start
node is a special case in that it is only allowed one transition.

It also defines a transition from the node1 node to the end node named approve.
The node1 node is of type participant. Nodes can have different types. In this case, the
participant type means that the workflow should wait here for the end user to decide what to do.
From a user interface viewpoint, this usually appears as a button that the user will click to advance the workflow.

When the end user clicks on approve, the workflow transitions to the end state and the workflow
completes.

The swimlane attribute is used to identify the actor (or end user) who is allowed to interact with the node.
In this case, the implicit initiator swimlane is used. This is an automatic swimlane which identifies
the user who launched the workflow. Thus, in this case, the initiator who started the workflow essentially ends up with
an approval process where they themselves do the approval. Not all that useful, in the end, but what the heck, it's
just a sample. For more examples of swimlanes, read on.

Swimlanes

A swimlane is used to define an actor who is participating in the workflow. In general, you will need to have one
swimlane per participant in the workflow. A swimlane gets its colorful name from a lane within
a swimming pool where only one person is allowed to swim in the lane at a time.

When a workflow node is of type participant, it will need to identify a swimlane that tells it which
actor is participating. By default, every workflow automatically has an implicit initiator swimlane
which contains the user who initiated the workflow.

Let's expand our example workflow to include a custom swimlane:

{
    "title": "Approve an article #2",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    }
}

The swimlanes configuration block identifies the swimlanes by ID and provides for each an array of
potential principals that can be actors within the swimlane. If the swimlane is empty (i.e. []) as shown above,
then any principal could fill the role. But none are selected by default.

If you try to start a workflow such as the one above, it will fail to start. You'll get an error back stating that
one or more of the swimlanes needs to be assigned before the workflow can be started. Indeed, before a workflow can
be started, all of its swimlanes must be assigned.

Note that once a workflow is started, it is possible to delegate authority to work on a node to another principal. It
is also possible as another principal to claim or unclaim authority. In either case, the other principal must be
identified within the swimlane actors array (or the array must be empty, meaning anyone can be assigned).

Here is another example where we've identified two users who fulfill the manager actor role:

{
    "title": "Approve an article #3",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": [
                "primary/pvenkman",
                "primary/estengler"
            ]
        }
    }
}

As before, in order to start this workflow, we first need to tell the workflow engine which actor will take on the
manager swimlane. Let's say that we pick primary/pvenkman. Now the workflow begins and
Peter Venkman is assigned the node1 approval task. He goes to his task screen and sees a task waiting
for him to work on.

At this this point, Peter might decide he needs to pass the task on to someone else. So he decides to delegate the
task. In doing so, he will be given a list of potential delegates and only Egon Stengler (primary/estengler)
will appear in the list. Similary, Peter may elect to unclaim the task. Unclaiming the task will release it to the
swimlane meaning that no one is working on it. However, both Peter Venkman and Egon Stengler will see the task
available to them as something they could start working on, if they wanted to. One of them simply needs to claim it.

While it is typical for swimlanes to map to participant nodes, it does not necessarily imply that you will have
one swimlane per node. In fact, swimlanes may often be used across many nodes in cases where a particular user who
worked on something earler may need to work on something again later. Swimlanes give your business process a means
for retaining information about who was involved in the process and involving them against at a later point.

Forms

A workflow maintains a JSON payload (or property set) that is modified during the flow and kept after the workflow
completes. As the workflow moves from one node to the next, it copies this JSON payload down into workflow tasks
so that it can be edited by separate users individually. When the workflow task is completed, the data is merged back
into the parent workflow instance. This allows many users to work on things at once, with each task worked upon
in isolation.

Each workflow has a set of standard properties and is fully customizable with your own properties. You can define
the schema as well as the layout options for forms utilized by the workflow.

The following properties are optional but always supported:

  • title
  • description
  • instructions
  • dueDate
  • priority

And you're free to add your own as you see fit.

Let's enhance our example workflow to include some custom form properties:

{
    "title": "Approve an article #4",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": [
                "primary/pvenkman",
                "primary/estengler"
            ]
        }
    },
    "forms": {
        "global": {
            "schema": {
                "type": "object",
                "properties": {
                    "topic": {
                        "type": "string",
                        "title": "Topic"
                    }
                }
            },
            "options": {
                "fields": {
                    "topic": {
                        "type": "textarea"
                    }
                }
            }
        }
    }
}

Properties are defined using JSON Schema. Cloud CMS natively works with JSON schema to server-side validate your
data and produce user-interface forms.

Above, we define a global form which is the top-level work that is globally scoped to the entire
workflow model. Every task will receive this form as a definition of any data bindings. The schema
sub-property provides the JSON schema of the data. It is required. The options sub-property is
optional and provides form configuration to drive the underlying Cloud CMS forms engine. For details on the
kinds of customizations possible, check out Alpaca Forms.

The form shown above is scoped to the workflow instance itself. If you wanted to define some properties at the
workflow instance level and other properties at the node level, you're free to do so. Just bear in mind that node level
properties inherit from workflow instance properties. You cannot define the same properties at both levels.

Here is an example that includes node-level properties:

{
    "title": "Approve an article #5",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "form": "node1form"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": [
                "primary/pvenkman",
                "primary/estengler"
            ]
        }
    },
    "forms": {
        "global": {
            "schema": {
                "type": "object",
                "properties": {
                    "topic": {
                        "type": "string",
                        "title": "Topic"
                    }
                }
            },
            "options": {
                "fields": {
                    "topic": {
                        "type": "textarea"
                    }
                }
            }
        },
        "node1form": {
            "schema": {
                "type": "object",
                "properties": {
                    "stars": {
                        "type": "number",
                        "title": "Rating"
                    }
                }
            }
        }
    }
}

If during the course of this workflow the end user works with forms generated from the properties as per the example
above, the resulting workflow JSON might include the following:

{
    "topic": "Agriculture",
    "stars": 3
}

Workflow Models and Deployment

Every workflow model has a model ID which uniquely identifies the workflow model. It can be any string you'd like
though convention is to use a QName (such as custom:workflow1). When you create a workflow model, it
is automatically tagged with a version number.

A workflow model can be modified until you're happy with it. To use it, it must then be deployed. Once a
workflow model is deployed, that version of the workflow model is frozen. It cannot be modified any longer under
the assumption that there may be in-flight workflow instances using this specific version of the model.
Those in-flight workflow instances require the workflow model to be available to provide routing instructions.

You can delete a deployed workflow model only if there are zero in-flight workflow instances.

To make changes to a deployed workflow model, you must first create a new version of the workflow model. The new
version of the workflow model has the same ID but a different version. Any current in-flight workflows continue to
use the old workflow model. Even after you deploy the new version of your model, any pre-existing in-flight workflows
will continue to use the old model where as new workflow instances will use the new model.

Workflow Node Types

Each workflow node defined in a workflow model must provide a type attribute that identifies the
type of workflow node being configured. The following workflow node types are supported:

Type Description
START The start node of a workflow. There should always be one START node. The START node must have at least one outgoing transition.
END The end node of a workflow. There should always be one END node. The END node must not have any outgoing transitions.
PARTICIPANT A node that requires end user interaction. A swimlane must be assigned. You can have as many PARTICIPANT nodes as you would like. A PARTICIPANT node may have as many outgoing transitions as you would like.
PASSTHRU A node that simply passes through. A PASSTHRU node must have exactly one outgoing transition.

Workflow Instances

A workflow instance is an in-flight process that is being managed by Cloud CMS. It is a live process that is actively
running and looking to a workflow model as a blueprint for what to do next. Each workflow instance retains a reference
to a specific version of a workflow model. As future models are deployed, they are incrementally versioned so that
pre-existing workflow instances continue to use the older workflow model until they finish.

Workflow Tasks

As a workflow instance is routed through the BPM engine, individual workflow tasks are created for your workflow model's
nodes. A task is a unit of work to be accomplished. For non-participant nodes, a workflow task is automatic and
simply processes in the background. For PARTICIPANT nodes, a workflow task is created and stays alive
until an end user interacts with the workflow task and tells it what to do.

Each workflow task has its own copy of the payload resources (nodes) and the process data (properties). When an end
user makes changes to the documents or properties, they do not affect any other users working on other tasks. In this
way, tasks can be parallelized so that multiple groups can work on multiple tasks at the same time.

When a task is completed, the payload resources and process data is merged back into the workflow instance.

Workflow Payload Resources

Each workflow instance can have zero or more payload resources. These are usually content nodes (such as a PDF or a
Word document that you attach for approval). Some workflow instances may not have any payload resources. These are
content-less flows and are used for doing things like routing requests or signaling change updates.

Payload resources are copied into workflow tasks upon creation, enabling multiple users to work together at the same
time without stepping over each other. When tasks are completed, the task instances are archived and a history snapshot
is computed showing differences accumuluated within each task of the workflow over time (changes to workflow data,
workflow resources, comments and more).

Workflow Comments

Each workflow can have zero or more comments attached to it. Comments are attached to workflow instances while
maintaining a reference to the workflow task in which they were created. Comments are also ordered and timestamped
in addition to supporting full query, sorting and pagination on retrieval.

When comments are made, a workflow history record is created automatically so that an accurate historical view of
modifications to the workflow is kept. This includes the addition or modification of comments.

Workflow History

As tasks are completed, no matter whether automatic or PARTICIPANT in nature, workflow history records
are created and stored in the background. In this way, every change to the data properties, payload documents, comments
or other elements of the workflow are logged.

When a workflow instance completes, it is possible then to consult the workflow history records to see a full set of
changes from the workflow instance's beginning through to it's end. The workflow history records comprise the full
set of adjustments that can be applied to the workflow instance at the beginning to reproduce the workflow instance
at the end, should you desire to do so.

Each workflow history record also contains information about the start time, end time and the user who made the change.
In coordination with the Cloud CMS auditing service (which logs data access at the service and object level), the
workflow history mechanism provides chronological and activity data that lets you audit your business processes to see
who worked on what, made what changes and at what time.

Along with other aspects of the workflow service within Cloud CMS (and frankly, along with pretty much everything else
in Cloud CMS), you are free to query, read, update, report and work with workflow history records as you see fit.

Workflow Events

Workflow events are raised as the workflow "flows" through its lifecycle. By default, there are no registered handlers
and so these go unnoticed. But, they provide a place for you to hook in custom business logic through the registration
of one or more workflow handlers per event type.

Workflow events are grouped into the following kinds:

  • workflow
  • task
  • transition

The following workflow events are raised during the lifecycle of a typical workflow:

Event ID Kind Description
START workflow Raised when a workflow starts
END workflow Raised when a workflow ends
ENTER task Raised when a workflow begins a task
LEAVE task Raised when a workflow finishes a task
TRANSITION transition Raised when a workflow is about to transition from a source task to a target task

Workflow Event Handlers

Workflow event handlers are registered within your workflow model at either the instance-scope or the task-scope.
As with other elements in your model, the task-scope inherits from the instance-scope which means that you are
allowed to globally declare task events (like ENTER or LEAVE) at the instance level if you
wish to. Doing so will apply the same task event handlers to all tasks.

Using our really simple example, let's add logger event handlers that fire when the workflow starts
(START) and when the first task completes (LEAVE).

{
    "title": "Approve an article #1",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "initiator",
            "handlers": {
                "LEAVE": [{
                    "type": "logger",
                    "config": {}
                }]
            }
        },
        "end": {
            "type": "end"
        }
    },
    "handlers": {
        "START": [{
            "type": "logger",
            "config": {}
        }]
    }
}

The handlers blocks in the sample above provide two pieces of information that Cloud CMS needs to know
in order to fire your handler. The first is the type and the second is the optional config.
Some handlers require config and others do not.

There are several out-of-the-box handlers available:

Workflow Configuration

When instantiating workflows programatically, the following optional runtime properties will be inherited by any
event handlers that rely upon these values:

  • runtime.applicationId
  • runtime.emailProviderId
  • runtime.repositoryId
  • runtime.branchId

Sending Emails

A common scenario is to have a workflow send emails to participants as it transitions from node to node within the
workflow graph. There are multiple ways to do this. One way, as you've seen, is to use an Email Handler to take
full control of how emails are sent. Another option is to take advantage of configuration-driven emails using
straight up JSON configuration.

Simple Emails

The easiest way to get started is to simply set email: true on the node definition for any
participant node within the workflow model. When set to true, a stock email will be sent to the
participant to let them know that they have been assigned a task. The stock email provides a link back into Cloud CMS
so that the email recipient can click on it to get right to work.

Here is an example of a simple workflow with stock emails turned on:

{
    "title": "Simple Email Example",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "email": true
        },
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    }
}

Configured Emails

You can also provide a JSON object that defines the email that is to be sent. The JSON object can provide the
following properties:

{
    "to": "<the recipient email address>",
    "body": "<the body of the email>",
    "subject": "<the subject of the email>",
    "cc": "<the CC address for the email>",
    "bcc": "<the BCC address for the email>"
}

If the to field is not specified, the participant's email address will be filled in automatically.

Here is an example of a simple workflow with configured emails turned on:

{
    "title": "Configured Email Example",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "email": {
                "subject": "Ahoy Lad! A task awaits ye!",
                "body": "Please <a href='${link}'>take a look at this task</a>"
            }
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    }
}

Configured Emails (Reuse)

You can also store your email configurations globally and reference them from within the node definition so as to reuse
them across various stages of your workflow.

Here is the same example with a named configuration for reuse.

{
    "title": "Configured Email Example",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "email": "notificationEmail"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    },
    "emails": {
        "notificationEmail": {
            "subject": "Ahoy Lad! A task awaits ye!",
            "body": "Please <a href='${link}'>take a look at this task</a>"
        }
    }
}

Email Templates

You might want to store your email templates within Cloud CMS and reuse them within your workflow. That way, you can
change your emails at any time without having to make adjustments to your workflow model.

You can do this by specifying a templateKey. The template key matches up to n:email-template
instances by looking them up either by _doc or by key.

Here is an example:

{
    "title": "Email Template Example",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "email": {
                "templateKey": "myEmailTemplateKey",
                "subject": "Hello World!"
            }
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    }
}

This will look for an n:email-template instance with key equal
to myEmailTemplateKey.

Email Template Variables

The fields of your email (such as subject or body) can take advantage of the workflow
model made available to you. The following variables are available to you depending on what type of workflow
lifecycle event is being handled:

For workflow emails, the following variables are available:

For task emails, the following variables are available:

  • workflow - (Workflow Instance) The workflow instance
  • workflowTask - (Workflow Task) The current workflow task
  • initiator - the principal who initiated the workflow
  • assignee - if the workflow task is assigned, the principal (either a user or group) that is the assignee
  • delegates - a list of principals who serve as delegates (possible recipients for the pooled task)
  • previousWorkflowTask - the prior workflow task (if applicable)
  • previousAssignee - the principal who was assigned to the prior workflow task (if applicable)

For transition emails, the following variables are available:

  • workflow - (Workflow Instance) The workflow instance
  • initiator - the principal who initiated the workflow
  • transition - (Workflow Transition) The workflow transition
  • sourceWorkflowTask - (Workflow Task) The source workflow task
  • sourceAssignee - if the source workflow task is assigned, the principal (either a user or group) that is the assignee
  • sourceDelegates - a list of principals who serve as delegates for the source workflow task
  • targetWorkflowTask - (Workflow Task) The target workflow task
  • targetAssignee - if the target workflow task is assigned, the principal (either a user or group) that is the assignee
  • targetDelegates - a list of principals who serve as delegates for the target workflow task
  • previousWorkflowTask - the prior workflow task (if applicable)
  • previousAssignee - the principal who was assigned to the prior workflow task (if applicable)