Workflow support
A workflow in GGCE is designed on top of ...Actions
and GGCE automatically schedules the next action(s) in the workflow when an action of the workflow is completed (not canceled as described in #474).
A workflow operates on one type of AbstractAction
declared in actionType
(e.g. org.gringlobal.model.InventoryAction
):
class Workflow extends CoooperatorOwnedModel {
/** Is this workflow active? */
@Column(name = "is_active", nullable = false)
@Pattern("^Y|N$")
private String isActive;
/** Unique workflow code/key */
@Column(length = 100, unique = true, nullable = false, modifiable = false)
public String code;
/** Contains the class name of target actions. For example "org.gringlobal.model.InventoryAction" **/
@Column(name = "action_type", length = 100, nullable = false, modifiable = false)
public String actionType;
A workflow is composed of a number of workflow steps and a step is defined as:
class WorkflowStep extends CoooperatorOwnedModel {
@ManyToOne
private Workflow workflow;
}
/**
* Every workflow needs to have exactly one start step.
*/
class WorkflowStartStep extends WorkflowStep {
}
/**
* Every workflow needs to have at least one end step.
*/
class WorkflowEndStep extends WorkflowStep {
}
/**
* {@code ActionStep} schedules a follow-up action when some action in this workflow is completed (not canceled).
*/
class ActionStep extends WorkflowStep {
/** The action to be scheduled in this workflow step. */
@Column(nullable = false, modifiable = false)
private String actionNameCode;
/** The assignee of the action scheduled in this workflow step. */
@Column(nullable = true, modifiable = false)
private AclSid actionAssignee;
}
The flow between workflow steps is defined as a List<WorkflowTransition> transitions
:
/**
* A directional link between two workflow steps.
*/
class WorkflowTransition {
/** Source step in workflow */
@ManyToOne(optional = false)
@JoinColumn(updatable = false)
private WorkflowStep origin;
/** Target step in the same workflow */
@ManyToOne(optional = false)
@JoinColumn(updatable = false)
private WorkflowStep target;
}
All workflow steps must appear at least once as an origin and a target, except WorkflowStartStep
which has no origin
and WorkflowEndStep
with no target
.
Updating workflows
- New steps can always be added to a workflow.
- A workflow can be removed if there are no
workflowStepId
foreign key constraint problems - We should have a way to "disconnect selected actions from a workflow" somewhere in the UI
Running the workflows
To initiate the workflow we find its WorkflowStartStep
and find the relevant list of transitions.targets to "execute". For ActionStep
we schedule a new action with actionStep.actionNameCode
(add it to the database). All newly scheduled actions have assignee = nextActionAsignee
.
We need to keep track of which workflow step registered the action. This requires that we add private WorkflowStep workflowStep
to AbstractAction
. This allows us to find all actions associated with this workflow or any specific workflow step.
When any action is completed (with isDone = 'Y'
) anywhere in GGCE we inspect if has a workflowStep
then look for the next WorkflowStep
s in the corresponding workflow where transitions.origin == action.workflowStep
and "execute" them (e.g. adding the next action). If isDone != 'Y'
then nothing happens and that branch of the workflow ends.
To run workflows we should inject the handling logic somewhere where we have access to both previous target
and updated
action data so we can check if there was a change in the data. This could be an aspect on BaseActionSupport#update
and BaseActionSupport#updateFast
, or perhaps on .Firehose
(no, it doesn't have access to previous state)