Toolbox.Workflow (toolbox v1.1.0)

Module provides API to define state machine based workflow. Workflow is represented by struct containing all configuration needed to handle state of individual instances of given workflow.

Workflow is defined by transitions, which contains all needed attributes (source node, target node, ..). Workflow defined by transitions needs to be built epxlicitly by calling build/1 function. This function validates all workflow dependencies and state machine structure.

Workflow definition produced by build/1 function can be used to create new instance of given workflow. Workflow instance is struct of:

  • unique id
  • status
  • state (map containing all relevant data, manipulated by then callback during status transition)
  • history (record of all transitions which given instance went through in the past)
  • history in default is map of %{"timestamp" => timestamp, "status" => binary}
  • history entry can be updated via update_history_entry callback defined in transition
  • possible transitions (list of all transitions which could be executed in close future)
  • possible transition in default is map of %{"timestamp" => -1 | timestamp, "status" => binary}

  • timestamp -1 represents non-timeoutable transition
  • possible transition entry can be updated via update_possible_transition callback defined in transition

Workflow transition can be defined by:

  • from (source status)
  • to (target status)
  • when (predicate used to select transition which will be executed)
    • there can be multiple when definitions in list, all definitions are connected with && relation
    • possible when definitions:
      • {Module, function}, where function accepts transition, instance and message as args
      • {:timeout, timeout}, where timeout is defined in milliseconds
      • {:=, [path, to, state, key], value}
      • {:>, [path, to, state, key], value}
      • {:<, [path, to, state, key], value}
      • {:<=, [path, to, state, key], value}
      • {:>=, [path, to, state, key], value}
      • {:contains, [path, to, state, key], value}
      • {:is_in, [path, to, state, key], list_value}
    • all callbacks should return boolean value
  • then (callback used to update workflow instance state during transition execution)
    • there can be multiple then definitions in list, all definitions are executed in given order
    • possible then definitions:
      • {Module, function}, where funciton accepts transition, instance and message as args
    • all calbacks should return {:ok, %{} = wf_instance_state} | {:error, reason}

  • side_effects (callback used to generate output actions during transition execution)
    • there can be multiple then definitions in list, all definitions are executed in given order
    • possible then definitions:
      • {Module, function}, where funciton accepts transition, instance and message as args
    • all calbacks should return {:ok, [OutputAction]} | {:error, reason}

  • update_history_entry (callback used to modify transition execution history entry)
    • there can be multiple then definitions in list, all definitions are executed in given order
    • possible then definitions:
      • {Module, function}, where funciton accepts history entry, transition, instance and message as args
    • all calbacks should return {:ok, %{} = updated_history_entry} | {:error, reason}

  • update_possible_transition (callback used in handle_message/3 to modify possible transition)
    • there can be multiple then definitions in list, all definitions are executed in given order
    • possible then definitions:
      • {Module, function}, where funciton accepts possible transtion, transition, instance and message as args
    • all calbacks should return {:ok, %{} = updated_possible_transition} | {:error, reason}

Workflow transition execution uses given workflow definition and message to update state of given instance. If no configured workflow transition matches, nothing will happend = instance state will remain the same.

Order of callback execution:

  1. when definitions of transitions in definition order
  2. then definitions of matching transition
  3. update history entry definitions of matching transition
  4. update possible transition definitions of matching transition
  5. side effects definitions of matching transition

Link to this section Summary

Functions

Workflow transition can be defined by keys in params

Finishes workflow definition, validates all configured dependencies and workflow structure

Uses given workflow definition and message to update state of given instance. If no configured workflow transition matches, nothing will happend = instance state will remain the same.

Creates new blank workflow definition

Creates new instance for given workflow

Link to this section Types

@type status() :: String.t()
@type t() :: %Toolbox.Workflow{
  built?: boolean(),
  statuses: [status()],
  terminal_statuses: [status()],
  transitions: transitions()
}
Link to this type

transitions()

@type transitions() :: %{required(status()) => [Toolbox.Workflow.Transition.t()]}

Link to this section Functions

Link to this function

add_transition(wf, params)

@spec add_transition(t(), Keyword.t()) :: t()

Workflow transition can be defined by keys in params:

  • from (source status)
    • required parameter
  • to (target status)
    • required parameter
  • when (predicate used to select transition which will be executed)
    • optional parameter
    • there can be multiple when definitions in list, all definitions are connected with && relation
    • possible when definitions:
      • {Module, function}, where function accepts transition, instance and message as args
      • {:timeout, timeout}, where timeout is defined in milliseconds
      • {:=, [path, to, state, key], value}
      • {:>, [path, to, state, key], value}
      • {:<, [path, to, state, key], value}
      • {:<=, [path, to, state, key], value}
      • {:>=, [path, to, state, key], value}
      • {:contains, [path, to, state, key], value}
      • {:is_in, [path, to, state, key], list_value}
    • all callbacks should return boolean value
  • then (callback used to update workflow instance state during transition execution)
    • optional parameter
    • there can be multiple then definitions in list, all definitions are executed in given order
    • possible then definitions:
      • {Module, function}, where funciton accepts transition, instance and message as args
    • all calbacks should return {:ok, %{} = wf_instance_state} | {:error, reason}

  • side_effects (callback used to generate output actions during transition execution)
    • optional parameter
    • there can be multiple then definitions in list, all definitions are executed in given order
    • possible then definitions:
      • {Module, function}, where funciton accepts transition, instance and message as args
    • all calbacks should return {:ok, [OutputAction]} | {:error, reason}

  • update_history_entry (callback used to modify transition execution history entry)
    • optional parameter
    • there can be multiple then definitions in list, all definitions are executed in given order
    • possible then definitions:
      • {Module, function}, where funciton accepts history entry, transition, instance and message as args
    • all calbacks should return {:ok, %{} = updated_history_entry} | {:error, reason}

  • update_possible_transition (callback used in handle_message/3 to modify possible transition)
    • optional parameter
    • there can be multiple then definitions in list, all definitions are executed in given order
    • possible then definitions:
      • {Module, function}, where funciton accepts possible transtion, transition, instance and message as args
    • all calbacks should return {:ok, %{} = updated_possible_transition} | {:error, reason}

@spec build(t()) ::
  {:ok, t()}
  | {:error, :transition_from_required}
  | {:error, :transition_to_required}
  | {:error, {:bad_callback, {atom(), atom()}}}
  | {:error, :multiple_init_statuses}

Finishes workflow definition, validates all configured dependencies and workflow structure

Link to this function

handle_message(workflow, message)

@spec handle_message(t(), Toolbox.Message.t()) ::
  {:ok, [Toolbox.Scenario.OutputAction.t()], Toolbox.Workflow.Instance.t()}
  | {:terminated, [Toolbox.Scenario.OutputAction.t()],
     Toolbox.Workflow.Instance.t()}
  | {:error, :not_built_yet}
  | {:error, :status_mismatch}

Uses given workflow definition and message to update state of given instance. If no configured workflow transition matches, nothing will happend = instance state will remain the same.

Order of callback execution:

  1. when definitions of transitions in definition order
  2. then definitions of matching transition
  3. update history entry definitions of matching transition
  4. update possible transition definitions of matching transition
  5. side effects definitions of matching transition
Link to this function

handle_message(wf, wf_inst, msg)

@spec new() :: t()

Creates new blank workflow definition

Link to this function

new_instance(wf, status, id, state, msg, options \\ [])

@spec new_instance(t(), status(), String.t(), map(), Toolbox.Message.t(), Keyword.t()) ::
  {:ok, [Toolbox.Scenario.OutputAction.t()], Toolbox.Workflow.Instance.t()}
  | {:terminated, [Toolbox.Scenario.OutputAction.t()],
     Toolbox.Workflow.Instance.t()}
  | {:error, :unknown_status}

Creates new instance for given workflow