/docs/schema.md
This document contains a brief overview of asyncmachine-go schema formats. JSON schemas can be found in /docs/jsonschema.
Schema-v0
- type safety NO
- reflection NO
- bootstrapped NO
- inheritance NO
- generated NO
- bootstrapped NO
Schemas for asyncmachine-go are optional and for simple things, we don’t need a typed schema (nor type safety), and we
can construct the machine like so:
mach := newMach("DryWaterWet", am.Schema{
"Wet": {
Require: am.S{"Water"},
},
"Dry": {
Remove: am.S{"Water"},
},
"Water": {
Add: am.S{"Wet"},
Remove: am.S{"Dry"},
},
})
All the state-related calls happen via raw strings. It’s good for prototyping and nothing else.
Schema-v1
- type safety YES
- reflection NO
- bootstrapped NO
- inheritance NO
- generated NO
- bootstrapped NO
The v1 is the simplest schema file format:
package states
import am "github.com/pancsta/asyncmachine-go/pkg/machine"
// S is a type alias for a list of state names.
type S = am.S
// States map defines relations and properties of states.
var States = am.Schema{
CreatingExpense: {Remove: GroupExpense},
ExpenseCreated: {Remove: GroupExpense},
WaitingForApproval: {
Auto: true,
Remove: GroupApproval,
},
ApprovalGranted: {Remove: GroupApproval},
PaymentInProgress: {
Auto: true,
Remove: GroupPayment,
},
PaymentCompleted: {Remove: GroupPayment},
}
// Groups of mutually exclusive states.
var (
GroupExpense = S{CreatingExpense, ExpenseCreated}
GroupApproval = S{WaitingForApproval, ApprovalGranted}
GroupPayment = S{PaymentInProgress, PaymentCompleted}
)
// #region boilerplate defs
// Names of all the states (pkg enum).
const (
CreatingExpense = "CreatingExpense"
ExpenseCreated = "ExpenseCreated"
WaitingForApproval = "WaitingForApproval"
ApprovalGranted = "ApprovalGranted"
PaymentInProgress = "PaymentInProgress"
PaymentCompleted = "PaymentCompleted"
)
// Names is an ordered list of all the state names.
var Names = S{CreatingExpense, ExpenseCreated, WaitingForApproval, ApprovalGranted, PaymentInProgress, PaymentCompleted}
// #endregion
Usage:
import ss "github.com/pancsta/asyncmachine-go/examples/temporal_expense/states"
// ...
h := &MachineHandlers{}
mach, err := am.NewCommon(ctx, "expense", ss.Schema, ss.Names, h, nil, nil)
Schema-v2
- type safety YES
- reflection YES
- bootstrapped YES
- inheritance YES
- generated NO
- bootstrapped YES
The v2 is the actual schema file format and should be used everywhere. The problem is the use of
reflection that causes issues with TinyGo. Bootstrappable by am-gen schema.
import (
am "github.com/pancsta/asyncmachine-go/pkg/machine"
"github.com/pancsta/asyncmachine-go/pkg/states"
. "github.com/pancsta/asyncmachine-go/pkg/states/global"
)
// ...
// ServerStatesDef contains all the states of the Client state machine.
type ServerStatesDef struct {
*am.StatesBase
// basics
// Ready - Client is fully connected to the server.
Ready string
// rpc
// Starting listening
RpcStarting string
// setting up RPC accepting
RpcAccepting string
// RPC is accepting or has accepted connections
RpcReady string
// RPC client connected (technically)
ClientConnected string
// RPC client fully usable
HandshakeDone string
// How many times the client requested a full sync.
MetricSync string
// TCP tunneled over websocket
WebSocketTunnel string
// inherit from SharedStatesDef
*SharedStatesDef
}
// ServerGroupsDef contains all the state groups of the Client state machine.
type ServerGroupsDef struct {
*SharedGroupsDef
// Rpc is a group for RPC ready states.
Rpc S
}
// ServerSchema represents all relations and properties of ClientStates.
var ServerSchema = SchemaMerge(
// inherit from SharedStruct
SharedSchema,
am.Schema{
ssS.ErrNetwork: {
Require: S{am.StateException},
Remove: S{ssS.ClientConnected},
},
// inject Server states into HandshakeDone
ssS.HandshakeDone: StateAdd(
SharedSchema[ssS.HandshakeDone],
am.State{
Require: S{ssS.ClientConnected},
// TODO why?
Remove: S{Exception},
}),
// Server
ssS.Start: {Add: S{ssS.RpcStarting}},
ssS.Ready: {
Auto: true,
Require: S{ssS.HandshakeDone, ssS.RpcReady},
},
ssS.RpcStarting: {
Require: S{ssS.Start},
Remove: sgS.Rpc,
},
ssS.RpcAccepting: {
Require: S{ssS.Start},
Remove: sgS.Rpc,
},
ssS.RpcReady: {
Require: S{ssS.Start},
Remove: sgS.Rpc,
},
ssS.ClientConnected: {
Require: S{ssS.RpcReady},
},
ssS.MetricSync: {Multi: true},
ssS.WebSocketTunnel: {},
})
// EXPORTS AND GROUPS
var (
ssS = am.NewStates(ServerStatesDef{})
sgS = am.NewStateGroups(ServerGroupsDef{
Rpc: S{ssS.RpcStarting, ssS.RpcAccepting, ssS.RpcReady},
}, SharedGroups)
// ServerStates contains all the states for the Client machine.
ServerStates = ssS
// ServerGroups contains all the state groups for the Client machine.
ServerGroups = sgS
)
See /tools/cmd/am-gen for schema bootstrapping commands.
YAML Schema
YAML schema files are useful for bootstrapping the project boilerplate via am-gen starter-kit.
Start:
BaseDBReady:
remove:
- BaseDBStarting
BaseDBSaving:
multi: true
BaseDBStarting:
remove:
- BaseDBReady
CharacterReady:
remove:
- RestoreCharacter
- GenCharacter
CheckStories:
multi: true
require:
- Start
CheckingMenuRefs:
multi: true
require:
- Start
RestoreCharacter:
GenCharacter:
YAML Machine
YAML machine files can be used to generate a schema via am-gen schema-from-file.
id: my-serialized-mach
state_names:
- Start
- BaseDBReady
- BaseDBSaving
time:
- 1
- 2
- 0
queue_tick: 25
machine_tick: 1
MachAddress URLs
Machine addresses are implemented in am-dbg and am-vis for convenient copy-pasta.
Format: mach://<mach-id>[/<tx-id>[/<step>]][?<query-params>]
- Host (
MachId): State machine identifier (e.g.mach://my-mach). - Path (
TxId,Step):/<tx-id>: Specific transition ID (e.g.mach://my-mach/tx-101)./<tx-id>/<step>: Step index within the transition (e.g.mach://my-mach/tx-101/2).
- Query Parameters:
t=<uint64>: Machine logical time (MachTime).q=<uint64>: Queue tick (QueueTick).ht=<RFC3339>: Wall-clock timestamp (HumanTime).state=<string>: Selected/highlighted state name.group=<string>: Selected/highlighted state group name.
Example:
mach://auth-service/tx-45/2?state=Ready&t=1450
