psm

package
v0.0.0-...-39dbd90 Latest Latest
Warning

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

Go to latest
Published: May 23, 2024 License: MIT Imports: 19 Imported by: 7

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrDuplicateChainedEventID = errors.New("duplicate chained event ID")
View Source
var ErrDuplicateEventID = errors.New("duplicate event ID")
View Source
var TxOptions = &sqrlx.TxOptions{
	Isolation: sql.LevelReadCommitted,
	Retryable: true,
	ReadOnly:  false,
}

Functions

This section is empty.

Types

type BuilderFrom

type BuilderFrom[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	// contains filtered or unexported fields
}

func (BuilderFrom[K, S, ST, SD, E, IE]) Hook

func (tb BuilderFrom[K, S, ST, SD, E, IE]) Hook(
	hook IStateHookHandler[K, S, ST, SD, E, IE],
) *TransitionBuilder[K, S, ST, SD, E, IE]

Hook is a shortcut for OnEvent().Hook() with the event type matching callback function.

func (BuilderFrom[K, S, ST, SD, E, IE]) Mutate

func (tb BuilderFrom[K, S, ST, SD, E, IE]) Mutate(
	transition ITransitionHandler[K, S, ST, SD, E, IE],
) *TransitionBuilder[K, S, ST, SD, E, IE]

Mutate is a shortcut for OnEvent().Mutate() with the event type matching callback function.

func (BuilderFrom[K, S, ST, SD, E, IE]) OnEvent

func (tb BuilderFrom[K, S, ST, SD, E, IE]) OnEvent(event string) *TransitionBuilder[K, S, ST, SD, E, IE]

OnEvent identifies a specific transition from state(s) for an event.

type DBStateMachine

type DBStateMachine[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	*StateMachine[K, S, ST, SD, E, IE]
	// contains filtered or unexported fields
}

DBStateMachine adds the 'Transaction' method to the state machine, which runs the transition in a new transaction from the state machine's database

func (*DBStateMachine[K, S, ST, SD, E, IE]) Transition

func (sm *DBStateMachine[K, S, ST, SD, E, IE]) Transition(ctx context.Context, event *EventSpec[K, S, ST, SD, E, IE]) (S, error)

Transition transitions the state machine in a new transaction from the state machine's database pool

type EntityTableSpec

type EntityTableSpec struct {
	TableName  string
	DataColumn string

	// PKFieldPaths is a list of 'field paths' which constitute the primary key
	// of the entity, dot-notated protobuf field names.
	PKFieldPaths []string
}

type EventSpec

type EventSpec[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	// Keys must be set, to identify the state machine.
	Keys K

	// EventID must be set for incomming events.
	EventID string

	// The inner PSM Event type. Must be set for incomming events.
	Event IE

	// The cause of the event. Must be set for incomming events.
	Cause *psm_pb.Cause

	// Optional, defaults to the system time (if Zero())
	Timestamp time.Time
}

type Eventer

type Eventer[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	Transitions []ITransition[K, S, ST, SD, E, IE]
	// contains filtered or unexported fields
}

Eventer is the inner state machine, independent of storage.

func (Eventer[K, S, ST, SD, E, IE]) FindTransition

func (ee Eventer[K, S, ST, SD, E, IE]) FindTransition(status ST, event E) (ITransition[K, S, ST, SD, E, IE], error)

func (*Eventer[K, S, ST, SD, E, IE]) Register

func (ee *Eventer[K, S, ST, SD, E, IE]) Register(transition ITransition[K, S, ST, SD, E, IE])

func (Eventer[K, S, ST, SD, E, IE]) RunEvent

func (ee Eventer[K, S, ST, SD, E, IE]) RunEvent(
	ctx context.Context,
	state S,
	spec *EventSpec[K, S, ST, SD, E, IE],
	callbacks ...eventerCallback[K, S, ST, SD, E, IE],
) error

func (*Eventer[K, S, ST, SD, E, IE]) ValidateEvent

func (ee *Eventer[K, S, ST, SD, E, IE]) ValidateEvent(event E) error

type GeneralStateHook

type GeneralStateHook[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] func(context.Context, sqrlx.Transaction, HookBaton[K, S, ST, SD, E, IE], S, E) error

GeneralStateHook is a StateHook that should do something after all or most transitions, e.g. publishing to a global event bus or updating a cache.

func (GeneralStateHook[K, S, ST, SD, E, IE]) Matches

func (hook GeneralStateHook[K, S, ST, SD, E, IE]) Matches(ST, E) bool

func (GeneralStateHook[K, S, ST, SD, E, IE]) RunStateHook

func (hook GeneralStateHook[K, S, ST, SD, E, IE]) RunStateHook(
	ctx context.Context,
	tx sqrlx.Transaction,
	baton HookBaton[K, S, ST, SD, E, IE],
	state S,
	event E,
) error

type HookBaton

type HookBaton[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] interface {
	SideEffect(outbox.OutboxMessage)
	ChainEvent(IE)
	FullCause() E
	AsCause() *psm_pb.Cause
}

type HookWrapper

type HookWrapper[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	// contains filtered or unexported fields
}

func (HookWrapper) Matches

func (ef HookWrapper) Matches(currentStatus ST, outerEvent E) bool

func (HookWrapper[K, S, ST, SD, E, IE]) RunStateHook

func (f HookWrapper[K, S, ST, SD, E, IE]) RunStateHook(
	ctx context.Context,
	tx sqrlx.Transaction,
	baton HookBaton[K, S, ST, SD, E, IE],
	state S,
	event E,
) error

type IEvent

type IEvent[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	Inner any,
] interface {
	proto.Message
	UnwrapPSMEvent() Inner
	SetPSMEvent(Inner) error
	PSMKeys() K
	SetPSMKeys(K)
	PSMMetadata() *psm_pb.EventMetadata
	PSMIsSet() bool
}

IEvent is the Event Wrapper, the top level which has metadata, foreign keys to the state, and the event itself. e.g. *testpb.FooEvent, the concrete proto message

type IInnerEvent

type IInnerEvent interface {
	IPSMMessage
	PSMEventKey() string
}

IInnerEvent is the typed event *interface* which is the set of all possible events for the state machine e.g. testpb.FooPSMEvent interface - this is generated by the protoc plugin in _psm.pb.go It is set at compile time specifically to the interface type.

type IKeyset

type IKeyset interface {
	IPSMMessage
	PSMFullName() string
}

type IPSMMessage

type IPSMMessage interface {
	proto.Message
	PSMIsSet() bool
}

IGenericProtoMessage is the base extensions shared by all message entities in the PSM generated code

type IState

type IState[K IKeyset, ST IStatusEnum, SD IStateData] interface {
	IPSMMessage
	GetStatus() ST
	SetStatus(ST)
	PSMMetadata() *psm_pb.StateMetadata
	PSMKeys() K
	SetPSMKeys(K)
	PSMData() SD
}

IState[K, ST, SD]is the main State Entity e.g. *testpb.FooState

type IStateData

type IStateData interface {
	IPSMMessage
}

IStateData is the Data Entity e.g. *testpb.FooStateData

type IStateHook

type IStateHook[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] interface {
	Matches(ST, E) bool
	RunStateHook(context.Context, sqrlx.Transaction, HookBaton[K, S, ST, SD, E, IE], S, E) error
}

type IStateHookHandler

type IStateHookHandler[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] interface {
	// contains filtered or unexported methods
}

type IStatusEnum

type IStatusEnum interface {
	~int32
	ShortString() string
	String() string
}

IStatusEnum is enum representing the named state of the entity. e.g. *testpb.FooStatus (int32)

type ITransition

type ITransition[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] interface {
	Matches(ST, E) bool
	RunTransition(context.Context, S, E) error
}

type ITransitionHandler

type ITransitionHandler[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] interface {
	// contains filtered or unexported methods
}

type PSMHookFunc

type PSMHookFunc[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
	SE IInnerEvent,
] func(context.Context, sqrlx.Transaction, HookBaton[K, S, ST, SD, E, IE], S, SE) error

type PSMMutationFunc

type PSMMutationFunc[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
	SE IInnerEvent,
] func(SD, SE) error

type PSMTableSpec

type PSMTableSpec[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	// Primary Key derives the *State* primary key, and thus event foreign key
	// to state, from the event.
	PrimaryKey      func(K) (map[string]interface{}, error)
	EventPrimaryKey func(string, K) (map[string]interface{}, error)

	State TableSpec[S]
	Event TableSpec[E]

	// When set, stores the current state in the event table.
	EventStateSnapshotColumn *string
}

PSMTableSpec is the configuration for the state machine's table mapping. The generated code provides a default which is derived from the patterns and annotations on the proto message, however, the proto message is designed to specify the wire data, not the storage mechanism, so consuming code may need to override some of the defaults to map to the database. The generated default is called DefaultFooPSMTableSpec

func (PSMTableSpec[K, S, ST, SD, E, IE]) StateTableSpec

func (spec PSMTableSpec[K, S, ST, SD, E, IE]) StateTableSpec() QueryTableSpec

StateTableSpec derives the Query spec table elements from the StateMachine specs. The Query spec is a subset of the TableSpec

func (PSMTableSpec[K, S, ST, SD, E, IE]) Validate

func (spec PSMTableSpec[K, S, ST, SD, E, IE]) Validate() error

type QuerySpec

type QuerySpec[
	GetREQ pquery.GetRequest,
	GetRES pquery.GetResponse,
	ListREQ pquery.ListRequest,
	ListRES pquery.ListResponse,
	ListEventsREQ pquery.ListRequest,
	ListEventsRES pquery.ListResponse,
] struct {
	QueryTableSpec

	ListRequestFilter       func(ListREQ) (map[string]interface{}, error)
	ListEventsRequestFilter func(ListEventsREQ) (map[string]interface{}, error)
}

QuerySpec is the configuration for the query service side of the state machine. Can be partially derived from the state machine table spec, but contains types relating to the query service so cannot be fully derived.

type QueryTableSpec

type QueryTableSpec struct {
	EventTypeName protoreflect.FullName
	StateTypeName protoreflect.FullName

	State EntityTableSpec
	Event EntityTableSpec
}

QueryTableSpec is the subset of TableSpec which does not relate to the specific event and state types of the state machine.

type SimpleSystemActor

type SimpleSystemActor struct {
	ID    uuid.UUID
	Actor protoreflect.Value
}

func MustSystemActor

func MustSystemActor(id string) SimpleSystemActor

func NewSystemActor

func NewSystemActor(id string) (SimpleSystemActor, error)

func (SimpleSystemActor) NewEventID

func (sa SimpleSystemActor) NewEventID(fromEventUUID string, eventKey string) string

type StateMachine

type StateMachine[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	*Eventer[K, S, ST, SD, E, IE]
	SystemActor SystemActor
	// contains filtered or unexported fields
}

StateMachine is a database wrapper around the eventer. Using sane defaults with overrides for table configuration.

func NewStateMachine

func NewStateMachine[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
](
	cb *StateMachineConfig[K, S, ST, SD, E, IE],
) (*StateMachine[K, S, ST, SD, E, IE], error)

func (*StateMachine[K, S, ST, SD, E, IE]) FindHooks

func (sm *StateMachine[K, S, ST, SD, E, IE]) FindHooks(status ST, event E) []IStateHook[K, S, ST, SD, E, IE]

func (*StateMachine[K, S, ST, SD, E, IE]) From

func (ee *StateMachine[K, S, ST, SD, E, IE]) From(states ...ST) BuilderFrom[K, S, ST, SD, E, IE]

From creates a new transition builder for the state machine, which will run when the machine is transitioning from one of the given states. If the given list is empty (From()), matches ALL starting states.

func (*StateMachine[K, S, ST, SD, E, IE]) GeneralHook

func (ee *StateMachine[K, S, ST, SD, E, IE]) GeneralHook(hook GeneralStateHook[K, S, ST, SD, E, IE])

func (StateMachine[K, S, ST, SD, E, IE]) StateTableSpec

func (sm StateMachine[K, S, ST, SD, E, IE]) StateTableSpec() QueryTableSpec

func (*StateMachine[K, S, ST, SD, E, IE]) Transition

func (sm *StateMachine[K, S, ST, SD, E, IE]) Transition(ctx context.Context, db Transactor, event *EventSpec[K, S, ST, SD, E, IE]) (S, error)

func (*StateMachine[K, S, ST, SD, E, IE]) TransitionInTx

func (sm *StateMachine[K, S, ST, SD, E, IE]) TransitionInTx(ctx context.Context, tx sqrlx.Transaction, event *EventSpec[K, S, ST, SD, E, IE]) (S, error)

TransitionInTx uses an existing transaction to transition the state machine.

func (*StateMachine[K, S, ST, SD, E, IE]) WithDB

func (sm *StateMachine[K, S, ST, SD, E, IE]) WithDB(db Transactor) *DBStateMachine[K, S, ST, SD, E, IE]

type StateMachineConfig

type StateMachineConfig[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	// contains filtered or unexported fields
}

StateMachineConfig allows the generated code to build a default machine, but expose options to the user to override the defaults

func NewStateMachineConfig

func NewStateMachineConfig[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
](
	defaultSpec PSMTableSpec[K, S, ST, SD, E, IE],
) *StateMachineConfig[K, S, ST, SD, E, IE]

func (*StateMachineConfig[K, S, ST, SD, E, IE]) EventStateSnapshotColumn

func (cb *StateMachineConfig[K, S, ST, SD, E, IE]) EventStateSnapshotColumn(name string) *StateMachineConfig[K, S, ST, SD, E, IE]

func (*StateMachineConfig[K, S, ST, SD, E, IE]) EventTableName

func (cb *StateMachineConfig[K, S, ST, SD, E, IE]) EventTableName(eventTable string) *StateMachineConfig[K, S, ST, SD, E, IE]

func (*StateMachineConfig[K, S, ST, SD, E, IE]) NewStateMachine

func (cb *StateMachineConfig[K, S, ST, SD, E, IE]) NewStateMachine() (*StateMachine[K, S, ST, SD, E, IE], error)

func (*StateMachineConfig[K, S, ST, SD, E, IE]) PrimaryKey

func (cb *StateMachineConfig[K, S, ST, SD, E, IE]) PrimaryKey(primaryKey func(K) (map[string]interface{}, error)) *StateMachineConfig[K, S, ST, SD, E, IE]

func (*StateMachineConfig[K, S, ST, SD, E, IE]) StateTableName

func (cb *StateMachineConfig[K, S, ST, SD, E, IE]) StateTableName(stateTable string) *StateMachineConfig[K, S, ST, SD, E, IE]

func (*StateMachineConfig[K, S, ST, SD, E, IE]) StoreEventStateSnapshot

func (cb *StateMachineConfig[K, S, ST, SD, E, IE]) StoreEventStateSnapshot() *StateMachineConfig[K, S, ST, SD, E, IE]

func (*StateMachineConfig[K, S, ST, SD, E, IE]) StoreExtraEventColumns

func (cb *StateMachineConfig[K, S, ST, SD, E, IE]) StoreExtraEventColumns(eventColumns func(E) (map[string]interface{}, error)) *StateMachineConfig[K, S, ST, SD, E, IE]

func (*StateMachineConfig[K, S, ST, SD, E, IE]) StoreExtraStateColumns

func (cb *StateMachineConfig[K, S, ST, SD, E, IE]) StoreExtraStateColumns(stateColumns func(S) (map[string]interface{}, error)) *StateMachineConfig[K, S, ST, SD, E, IE]

func (*StateMachineConfig[K, S, ST, SD, E, IE]) SystemActor

func (cb *StateMachineConfig[K, S, ST, SD, E, IE]) SystemActor(systemActor SystemActor) *StateMachineConfig[K, S, ST, SD, E, IE]

type StateQueryOptions

type StateQueryOptions struct {
	Auth       pquery.AuthProvider
	AuthJoin   *pquery.LeftJoin
	SkipEvents bool
}

type StateQuerySet

type StateQuerySet[
	GetREQ pquery.GetRequest,
	GetRES pquery.GetResponse,
	ListREQ pquery.ListRequest,
	ListRES pquery.ListResponse,
	ListEventsREQ pquery.ListRequest,
	ListEventsRES pquery.ListResponse,
] struct {
	Getter      *pquery.Getter[GetREQ, GetRES]
	MainLister  *pquery.Lister[ListREQ, ListRES]
	EventLister *pquery.Lister[ListEventsREQ, ListEventsRES]
}

StateQuerySet is a shortcut for manually specifying three different query types following the 'standard model': 1. A getter for a single state 2. A lister for the main state 3. A lister for the events of the main state

func BuildStateQuerySet

func BuildStateQuerySet[
	GetREQ pquery.GetRequest,
	GetRES pquery.GetResponse,
	ListREQ pquery.ListRequest,
	ListRES pquery.ListResponse,
	ListEventsREQ pquery.ListRequest,
	ListEventsRES pquery.ListResponse,
](
	smSpec QuerySpec[GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES],
	options StateQueryOptions,
) (*StateQuerySet[GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES], error)

func (*StateQuerySet[GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES]) Get

func (gc *StateQuerySet[
	GetREQ, GetRES,
	ListREQ, ListRES,
	ListEventsREQ, ListEventsRES,
]) Get(ctx context.Context, db Transactor, reqMsg GetREQ, resMsg GetRES) error

func (*StateQuerySet[GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES]) List

func (gc *StateQuerySet[
	GetREQ, GetRES,
	ListREQ, ListRES,
	ListEventsREQ, ListEventsRES,
]) List(ctx context.Context, db Transactor, reqMsg proto.Message, resMsg proto.Message) error

func (*StateQuerySet[GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES]) ListEvents

func (gc *StateQuerySet[
	GetREQ, GetRES,
	ListREQ, ListRES,
	ListEventsREQ, ListEventsRES,
]) ListEvents(ctx context.Context, db Transactor, reqMsg proto.Message, resMsg proto.Message) error

type SystemActor

type SystemActor interface {
	NewEventID(fromEventUUID string, eventKey string) string
}

type TableSpec

type TableSpec[T proto.Message] struct {
	TableName         string
	StoreExtraColumns func(T) (map[string]interface{}, error)
	// DataColumn is the JSONB column which stores the main data chunk
	DataColumn   string
	PKFieldPaths []string
}

type Transactor

type Transactor interface {
	Transact(context.Context, *sqrlx.TxOptions, sqrlx.Callback) error
}

type TransitionBuilder

type TransitionBuilder[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	// contains filtered or unexported fields
}

func (*TransitionBuilder[K, S, ST, SD, E, IE]) Hook

func (tb *TransitionBuilder[K, S, ST, SD, E, IE]) Hook(
	hook IStateHookHandler[K, S, ST, SD, E, IE],
) *TransitionBuilder[K, S, ST, SD, E, IE]

Hook creates and registers a new hook to run after the transition. Multiple hooks can be attached to the same transition.

func (*TransitionBuilder[K, S, ST, SD, E, IE]) Mutate

func (tb *TransitionBuilder[K, S, ST, SD, E, IE]) Mutate(
	transition ITransitionHandler[K, S, ST, SD, E, IE],
) *TransitionBuilder[K, S, ST, SD, E, IE]

Mutate sets the function which will merge data from the event into the state data. A transition can have zero or one mutate function, calling this function replaces any previous function.

func (*TransitionBuilder[K, S, ST, SD, E, IE]) Noop

func (tb *TransitionBuilder[K, S, ST, SD, E, IE]) Noop() *TransitionBuilder[K, S, ST, SD, E, IE]

Noop registers a transition which does nothing, but prevents the machine from erroring when the conditions are met.

func (*TransitionBuilder[K, S, ST, SD, E, IE]) SetStatus

func (tb *TransitionBuilder[K, S, ST, SD, E, IE]) SetStatus(
	status ST,
) *TransitionBuilder[K, S, ST, SD, E, IE]

SetStatus sets status which the state machine will have after the transition. Not all transitions will change the status, some will only mutate. Calling this function replaces any previous value.

type TransitionWrapper

type TransitionWrapper[
	K IKeyset,
	S IState[K, ST, SD],
	ST IStatusEnum,
	SD IStateData,
	E IEvent[K, S, ST, SD, IE],
	IE IInnerEvent,
] struct {
	// contains filtered or unexported fields
}

func (TransitionWrapper) Matches

func (ef TransitionWrapper) Matches(currentStatus ST, outerEvent E) bool

func (TransitionWrapper[K, S, ST, SD, E, IE]) RunTransition

func (f TransitionWrapper[K, S, ST, SD, E, IE]) RunTransition(
	ctx context.Context,
	state S,
	event E,
) error

Jump to

Keyboard shortcuts

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