Workflow Specification
Overview
Grid Workflow enables users to model business processes as state transitions within smart contracts. Its design decouples the complex logic of business processes from smart contract logic, simplifying the creation of each.
Workflow definitions are comprised of states, constraints, and permissions. Workflow states represent the stages of a business process. Constraints represent conditions that must be met to transition from one state to the next. Permission aliases define the actions that each party can take at each state in the workflow. Workflows contain one or more sub-workflows, which further help to encapsulate business process complexity.
Representation
Workflow
Workflow
consists of a list of SubWorkflow
:
pub struct Workflow {
subworkflow: Vec<SubWorkflow>,
}
impl Workflow {
pub fn new(subworkflow: Vec<SubWorkflow>) -> Self {
Self { subworkflow }
}
pub fn subworkflow(&self, name: &str) -> Option<SubWorkflow> {
for sub_wf in &self.subworkflow {
if sub_wf.name() == name {
return Some(sub_wf.clone());
}
}
None
}
}
SubWorkflow
SubWorkflow
has a name, a list of WorkflowState
and a list of starting
states:
#[derive(Clone)]
pub struct SubWorkflow {
name: String,
states: Vec<WorkflowState>,
starting_states: Vec<String>,
}
impl SubWorkflow {
pub fn name(&self) -> &str {
&self.name
}
pub fn state(&self, name: &str) -> Option<WorkflowState> {
for state in &self.states {
if state.name() == name {
return Some(state.clone());
}
}
None
}
pub fn starting_states(&self) -> &[String] {
&self.starting_states
}
}
WorkflowState
WorkflowState
has a name, list of constraints, list of permission aliases,
and list of transitions that can be made from that state. It has four methods:
-
can_transition
returns true if an entity can execute a transition to a given state given its Pike permissions. -
expand_permissions
returns a list of all permissions that are stored under a givenPermissionAlias
-
get_aliases_by_permission
retrieves all aliases defined within this state that contain the specified workflow permission -
has_constraint
returns true if the workflow state has the specified constraint
#[derive(Clone)]
pub struct WorkflowState {
name: String,
constraints: Vec<String>,
permission_aliases: Vec<PermissionAlias>,
transitions: Vec<String>,
}
impl WorkflowState {
pub fn can_transition(&self, new_state: String, pike_permissions: &[String]) -> bool {
if self.name == new_state {
return true;
}
if !self.transitions.contains(&new_state) {
return false;
}
for perm in pike_permissions {
for alias in &self.permission_aliases {
if alias.name() == perm && alias.transitions.contains(&new_state) {
return true;
}
}
}
false
}
pub fn expand_permissions(&self, names: &[String]) -> Vec<String> {
let mut perms = Vec::new();
for name in names {
for alias in &self.permission_aliases {
if alias.name() == name {
perms.append(&mut alias.permissions().to_vec());
}
}
}
perms
}
pub fn get_aliases_by_permission(&self, permission: &str) -> Vec<String> {
let mut aliases = Vec::new();
for alias in &self.permission_aliases {
if alias.permissions().contains(&permission.to_string()) {
aliases.push(alias.name().to_string());
}
}
aliases
}
pub fn has_constraint(&self, constraint: &str) -> bool {
self.constraints.contains(&constraint.to_string())
}
}
PermissionAlias
PermissionAlias
is an alias that houses multiple permissions. It has a name, a
list of actions that live under the alias, and a list of transitions the alias
can perform:
#[derive(Clone, Default)]
pub struct PermissionAlias {
name: String,
permissions: Vec<String>,
transitions: Vec<String>,
}
impl PermissionAlias {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
permissions: vec![],
transitions: vec![],
}
}
pub fn add_permission(&mut self, permission: &str) {
self.permissions.push(permission.to_string());
}
pub fn add_transition(&mut self, transition: &str) {
self.transitions.push(transition.to_string());
}
}
Below is an example of a permission alias with the name po::seller
that has
permission to perform two actions, create a purchase order version and
transition the purchase order to an issued state, and the ability to
perform one transition, moving the purchase order to its issued state:
let mut seller = PermissionAlias::new("po::seller");
seller.add_permission(&Permission::CanCreatePoVersion.to_string());
seller.add_permission(&Permission::CanTransitionIssued.to_string());
seller.add_transition("issued");