state_machine

package module
v0.0.0-...-b2751e8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2023 License: MIT Imports: 6 Imported by: 0

README

state-machine

Build Status | codecov | Go Report Card | GoDoc

A simple state machine.

If i miss something or you have something interesting, please be part of this project. Let me know! My contact is at the end.

With support for

  • Roles
  • State Machines
  • Transitions
  • Handlers

With configuration options

  • WithLogger
  • WithLogLevel

With methods

  • NewAddHandlers, to add handlers
  • NewStateMachine, to add a new state machine
  • NewCheckTransition, to check a transition
  • NewTransition, to make a transition
  • NewGetTransitions, to get the allowed transitions from the current state

With manual handlers

Executed on Check Transition (method NewCheckTransition)
  • BeforeCheck, execute before check
Executed on Transition (method NewTransition)
  • BeforeExecute, execute before execute
  • AfterExecute, execute after execute
  • OnSuccess, execute on success
  • OnError, execute on error

Dependency Management

Dep

Project dependencies are managed using Dep. Read more about Dep.

  • Install dependencies: dep ensure
  • Update dependencies: dep ensure -update
Go
go get github.com/joaosoft/state-machine

Usage

This example is available in the project at state-machine/examples

Configuration
State machine A (yaml)
View file
state_machine:
  -
    id: "1"
    name: "New"
    transitions:
      -
        id: "2"
        load:
          - "load_dummy"
        check:
          -
            "check_new_to_in-progress"
        execute:
          -
            "execute_new_to_in-progress"
  -
    id: "2"
    name: "In progress"
    transitions:
      -
        id: "3"
        check:
          -
            "check_in-progress_to_approved"
        execute:
          -
            "execute_in-progress_to_approved"
        events:
          success:
            -
              "event_success_in-progress_to_approved"
          error:
            -
              "event_error_in-progress_to_approved"
      -
        id: "4"
        check:
          -
            "check_in-progress_to_denied"
        execute:
          -
            "execute_in-progress_to_denied"
        events:
          success:
            -
              "event_success_in-progress_to_denied"
          error:
            -
              "event_error_in-progress_to_denied"
  -
    id: "3"
    name: "Approved"
  -
    id: "4"
    name: "Denied"

roles:
  operator:
    -
      id: "1"
      transitions:
        -
          id: "2"
          execute:
            - "execute_new_to_in-progress_role"
          events:
            success:
              - "event_success_new_to_in-progress_role"
    -
      id: "2"
      transitions:
        -
          id: "3"
        -
          id: "4"
State machine B (json)
View file
{
  "state_machine": [
    {
      "id": "1",
      "name": "Todo",
      "transitions": [
        {
          "id": "2",
          "check": [
            "check_todo_to_in-development"
          ],
          "execute": [
            "execute_todo_to_in-development"
          ]
        }
      ]
    },
    {
      "id": "2",
      "name": "In development",
      "transitions": [
        {
          "id": "3",
          "check": [
            "check_in-development_to_done"
          ],
          "execute": [
            "execute_in-development_to_done"
          ],
          "events": {
            "success": [
              "event_success_in-development_to_done"
            ],
            "error": [
              "event_error_in-development_to_done"
            ]
          }
        },
        {
          "id": "4",
          "check": [
            "check_in-development_to_canceled"
          ],
          "execute": [
            "execute_in-development_to_canceled"
          ],
          "events": {
            "success": [
              "event_success_in-development_to_canceled"
            ],
            "error": [
              "event_error_in-development_to_canceled"
            ]
          }
        }
      ]
    },
    {
      "id": "3",
      "name": "Done"
    },
    {
      "id": "4",
      "name": "Canceled"
    }
  ],
  "roles": {
    "worker": [
      {
        "id": "1",
        "transitions": [
          {
            "id": "2"
          }
        ]
      },
      {
        "id": "2",
        "transitions": [
          {
            "id": "3"
          },
          {
            "id": "4"
          }
        ]
      }
    ]
  }
}
Implementation
View code
const (
	StateMachineA     state_machine.StateMachineType = "A"
	RoleStateMachineA state_machine.RoleType         = "operator"

	StateMachineB     state_machine.StateMachineType = "B"
	RoleStateMachineB state_machine.RoleType         = "worker"
)

func init() {
	// :: add handlers

	// state machine A
	fmt.Println(":: State Machine: A - Adding handlers")
	state_machine.NewAddHandlers(StateMachineA).
		Load("load_dummy", loadDummy).
		//
		Check("check_new_to_in-progress", checkNewToInProgress).
		Check("check_in-progress_to_approved", checkInProgressToApproved).
		Check("check_in-progress_to_denied", checkInProgressToDenied).
		//
		Execute("execute_new_to_in-progress", executeNewToInProgress).
		Execute("execute_new_to_in-progress_role", executeNewToInProgressByRole).
		Execute("execute_in-progress_to_approved", executeInProgressToApproved).
		Execute("execute_in-progress_to_denied", executeInProgressToDenied).
		//
		EventSuccess("event_success_new_to_in-progress_role", eventOnSuccessNewToInProgressByRole).
		EventSuccess("event_success_new_to_in-progress", eventOnSuccessNewToInProgress).
		EventSuccess("event_success_in-progress_to_approved", eventOnSuccessInProgressToApproved).
		EventSuccess("event_success_in-progress_to_denied", eventOnSuccessInProgressToDenied).
		//
		EventError("event_error_new_to_in-progress", eventOnErrorNewToInProgress).
		EventError("event_error_in-progress_to_approved", eventOnErrorInProgressToApproved).
		EventError("event_error_in-progress_to_denied", eventOnErrorInProgressToDenied)

	// state machine B
	fmt.Println(":: State Machine: B - Adding handlers")
	state_machine.NewAddHandlers(StateMachineB).
		Manual(beforeExecuteLoadFromState, state_machine.BeforeCheck, state_machine.BeforeExecute).
		//
		Check("check_todo_to_in-development", checkTodoToInDevelopment).
		Check("check_in-development_to_done", checkInDevelopmentToDone).
		Check("check_in-development_to_canceled", checkInDevelopmentToCanceled).
		//
		Execute("execute_todo_to_in-development", executeTodoToInDevelopment).
		Execute("execute_in-development_to_canceled", executeInDevelopmentToCanceled).
		Execute("execute_in-development_to_done", executeInDevelopmentToDone).
		//
		EventSuccess("event_success_todo_to_in-development", eventOnSuccessTodoToInDevelopment).
		EventSuccess("event_success_in-development_to_done", eventOnSuccessInDevelopmentToDone).
		EventSuccess("event_success_in-development_to_canceled", eventOnSuccessInDevelopmentToCanceled).
		//
		EventError("event_error_todo_to_in-development", eventOnErrorTodoToInDevelopment).
		EventError("event_error_in-development_to_done", eventOnErrorInDevelopmentToDone).
		EventError("event_error_in-development_to_canceled", eventOnErrorInDevelopmentToCanceled)

	// :: add state machines

	// A
	fmt.Println(":: State Machine: A - Adding state machine")
	if err := state_machine.NewStateMachine().
		Key(StateMachineA).
		File("/config/state_machines/state_machine_a.yaml").
		TransitionHandler(StateMachineATransitionHandler).
		Load(); err != nil {
		panic(err)
	}

	// B
	fmt.Println(":: State Machine: B - Adding state machine")
	if err := state_machine.NewStateMachine().
		Key(StateMachineB).
		File("/config/state_machines/state_machine_b.json").
		TransitionHandler(StateMachineBTransitionHandler).
		Load(); err != nil {
		panic(err)
	}
}

func main() {
	stateMachines := []state_machine.StateMachineType{StateMachineA, StateMachineB}
	stateMachinesRoles := []state_machine.RoleType{RoleStateMachineA, RoleStateMachineB}
	maxLen := 4
	ok := false

	// get all transitions of state machine A
	fmt.Println("\n:: State Machine: A - get all transition from 1 to 2")
	transitions, err := state_machine.NewGetTransitions().
		Role(RoleStateMachineA).
		StateMachine(StateMachineA).
		From("1").
		Execute()
	if err != nil {
		panic(err)
	}
	for _, transition := range transitions {
		fmt.Printf("can make transition to %s\n", transition.Name)
	}

	// check transitions of state machines
	for index, stateMachine := range stateMachines {
		fmt.Printf("\n:: State Machine: %s - check transitions\n", stateMachine)
		for i := 1; i <= maxLen; i++ {
			for j := maxLen; j >= 1; j-- {
				ok, err := state_machine.NewCheckTransition().
					Role(stateMachinesRoles[index]).
					StateMachine(stateMachine).
					From(strconv.Itoa(i)).
					To(strconv.Itoa(j)).
					Execute(1, "text", true)
				if err != nil {
					panic(err)
				}
				fmt.Printf("transition from %d to %d  with role %s ? %t\n", i, j, stateMachinesRoles[index], ok)
			}
		}
	}

	// check transaction - state machine B - from the state loaded by method 'beforeExecuteLoadFromState' to state 2
	fmt.Println("\n:: State Machine: B - check transition from state 1 (loaded) to state 2")
	ok, err = state_machine.NewTransition().
		Role(RoleStateMachineB).
		StateMachine(StateMachineB).
		To("2").
		Execute(1, "text", true)
	if err != nil {
		panic(err)
	}

	if !ok {
		fmt.Println("transition !ok")
	}

	// execute transaction - state machine B - from the state loaded by method 'beforeExecuteLoadFromState' to state 2
	fmt.Println("\n:: State Machine: B - making transition from state 1 (loaded) to state 2")
	ok, err = state_machine.NewTransition().
		Role(RoleStateMachineB).
		StateMachine(StateMachineB).
		To("2").
		Execute(1, "text", true)
	if err != nil {
		panic(err)
	}

	if !ok {
		fmt.Println("transition !ok")
	}
}
Result
View result
:: State Machine: A - Adding handlers
:: State Machine: B - Adding handlers
:: State Machine: A - Adding state machine
:: State Machine: B - Adding state machine

:: State Machine: A - get all transition from 1 to 2
can make transition to In progress

:: State Machine: A - check transitions
transition from 1 to 4  with role operator ? false
transition from 1 to 3  with role operator ? false
check in-progress handler with [1 text true]
transition from 1 to 2  with role operator ? true
transition from 1 to 1  with role operator ? false
check in-progress to denied handler with [1 text true]
transition from 2 to 4  with role operator ? true
check in-progress to approved handler with [1 text true]
transition from 2 to 3  with role operator ? true
transition from 2 to 2  with role operator ? false
transition from 2 to 1  with role operator ? false
transition from 3 to 4  with role operator ? false
transition from 3 to 3  with role operator ? false
transition from 3 to 2  with role operator ? false
transition from 3 to 1  with role operator ? false
transition from 4 to 4  with role operator ? false
transition from 4 to 3  with role operator ? false
transition from 4 to 2  with role operator ? false
transition from 4 to 1  with role operator ? false

:: State Machine: B - check transitions
load 'from' state handler with [1 text true]
transition from 1 to 4  with role worker ? false
load 'from' state handler with [1 text true]
transition from 1 to 3  with role worker ? false
load 'from' state handler with [1 text true]
check in-development handler with [1 text true]
transition from 1 to 2  with role worker ? true
load 'from' state handler with [1 text true]
transition from 1 to 1  with role worker ? false
load 'from' state handler with [1 text true]
transition from 2 to 4  with role worker ? false
load 'from' state handler with [1 text true]
transition from 2 to 3  with role worker ? false
load 'from' state handler with [1 text true]
check in-development handler role [1 text true]
transition from 2 to 2  with role worker ? true
load 'from' state handler with [1 text true]
transition from 2 to 1  with role worker ? false
load 'from' state handler with [1 text true]
transition from 3 to 4  with role worker ? false
load 'from' state handler with [1 text true]
transition from 3 to 3  with role worker ? false
load 'from' state handler with [1 text true]
check in-development handler with [1 text true]
transition from 3 to 2  with role worker ? true
load 'from' state handler with [1 text true]
transition from 3 to 1  with role worker ? false
load 'from' state handler with [1 text true]
transition from 4 to 4  with role worker ? false
load 'from' state handler with [1 text true]
transition from 4 to 3  with role worker ? false
load 'from' state handler with [1 text true]
check in-development handler with [1 text true]
transition from 4 to 2  with role worker ? true
load 'from' state handler with [1 text true]
transition from 4 to 1  with role worker ? false

:: State Machine: B - check transition from state 1 (loaded) to state 2
load 'from' state handler with [1 text true]
check in-development handler with [1 text true]
execute in-development handler with [1 text true]
state machine: B, transition handler with [1 text true]

:: State Machine: B - making transition from state 1 (loaded) to state 2
load 'from' state handler with [1 text true]
check in-development handler with [1 text true]
execute in-development handler with [1 text true]
state machine: B, transition handler with [1 text true]

Known issues

Follow me at

Facebook: https://www.facebook.com/joaosoft

LinkedIn: https://www.linkedin.com/in/jo%C3%A3o-ribeiro-b2775438/

If you have something to add, please let me know joaosoft@gmail.com

License

Released under the MIT License.

Documentation

Index

Constants

View Source
const (
	BeforeExecute manualHandlerKey = iota
	AfterExecute
	BeforeCheck
	OnSuccess
	OnError
)

Variables

This section is empty.

Functions

func NewAddHandlers

func NewAddHandlers(stateMachine StateMachineType) *addHandler

func NewCheckTransition

func NewCheckTransition() *newCheckTransition

func NewGetTransitions

func NewGetTransitions() *newGetTransitions

func NewStateMachine

func NewStateMachine() *newStateMachine

func NewTransition

func NewTransition() *newTransition

func WithLogLevel

func WithLogLevel(level logger.Level) stateMachineOption

WithLogLevel ...

func WithLogger

func WithLogger(logger logger.ILogger) stateMachineOption

WithLogger ...

Types

type CheckHandler

type CheckHandler func(ctx *Context) (bool, error)

type Context

type Context struct {
	Role         RoleType
	StateMachine StateMachineType
	From         string
	To           string
	Resource     interface{}
	Args         []interface{}
}

type EventErrorHandler

type EventErrorHandler func(ctx *Context, err error) error

type EventSuccessHandler

type EventSuccessHandler func(ctx *Context) error

type ExecuteHandler

type ExecuteHandler func(ctx *Context) error

type LoadHandler

type LoadHandler func(ctx *Context) error

type ManualHandler

type ManualHandler func(ctx *Context) error

type RoleType

type RoleType string

type StateMachineType

type StateMachineType string

type Transition

type Transition struct {
	Id   string `json:"id"`
	Name string `json:"name"`
	// contains filtered or unexported fields
}

type TransitionHandler

type TransitionHandler func(ctx *Context) error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL