blasphem/pkg/flow/flow.go

202 lines
4.2 KiB
Go
Raw Normal View History

2022-11-20 08:49:24 -05:00
// flow is the data entry flow.
package flow
import (
"fmt"
"time"
"dynatron.me/x/blasphem/internal/generate"
)
type (
2022-11-20 12:51:26 -05:00
ResultType string
FlowID string
Step string
HandlerKey string
Errors interface{}
2022-11-20 08:49:24 -05:00
Context interface{}
FlowStore map[FlowID]Handler
FlowManager struct {
flows FlowStore
}
2022-11-20 12:51:26 -05:00
Result struct {
Type ResultType `json:"type"`
2022-11-20 08:49:24 -05:00
ID FlowID `json:"flow_id"`
Handler []*HandlerKey `json:"handler"`
Title *string `json:"title,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
StepID *Step `json:"step_id,omitempty"`
2022-11-20 12:51:26 -05:00
Schema []SchemaItem `json:"data_schema"`
2022-11-20 08:49:24 -05:00
Extra *string `json:"extra,omitempty"`
Required *bool `json:"required,omitempty"`
Errors interface{} `json:"errors"`
Description *string `json:"description,omitempty"`
DescPlace *string `json:"description_placeholders"`
URL *string `json:"url,omitempty"`
Reason *string `json:"reason,omitempty"`
Context *string `json:"context,omitempty"`
Result interface{} `json:"result,omitempty"`
LastStep *string `json:"last_step"`
Options map[string]interface{} `json:"options,omitempty"`
Version *int `json:"version,omitempty"`
}
2022-11-20 12:51:26 -05:00
SchemaItem struct {
2022-11-20 08:49:24 -05:00
Type string `json:"type"`
Name string `json:"name"`
Required bool `json:"required"`
}
2022-11-20 12:51:26 -05:00
Schema []SchemaItem
2022-11-20 08:49:24 -05:00
)
type (
Schemer interface {
2022-11-20 12:51:26 -05:00
FlowSchema() Schema
2022-11-20 08:49:24 -05:00
}
Handler interface {
2022-12-17 15:17:05 -05:00
BaseHandler() FlowHandler
2022-11-20 08:49:24 -05:00
FlowID() FlowID
2022-11-20 12:51:26 -05:00
2022-11-20 08:49:24 -05:00
flowCtime() time.Time
}
)
const (
StepInit Step = "init"
)
2022-11-20 12:51:26 -05:00
func (fs *Schema) CheckRequired(rm map[string]interface{}) error {
2022-11-20 08:49:24 -05:00
for _, si := range *fs {
if si.Required {
if _, ok := rm[si.Name]; !ok {
return fmt.Errorf("missing required param %s", si.Name)
}
}
}
return nil
}
func NewFlowManager() *FlowManager {
return &FlowManager{
flows: make(FlowStore),
}
}
func stepPtr(s Step) *Step { return &s }
2022-11-20 13:42:10 -05:00
type FlowHandler struct {
2022-11-20 12:51:26 -05:00
ID FlowID // ID is the FlowID
Handler HandlerKey // Handler key
Context Context // flow Context
Schema Schema
2022-11-20 08:49:24 -05:00
// curStep is the current step set by the flow manager
curStep Step
ctime time.Time
}
2022-11-20 13:42:10 -05:00
func (f *FlowHandler) Step() Step { return f.curStep }
2022-11-20 08:49:24 -05:00
2022-12-17 15:17:05 -05:00
func (f *FlowHandler) BaseHandler() FlowHandler { return *f }
2022-11-20 08:49:24 -05:00
2022-11-20 13:42:10 -05:00
func (f *FlowHandler) FlowID() FlowID {
2022-11-20 08:49:24 -05:00
return f.ID
}
2022-11-20 13:42:10 -05:00
func (f *FlowHandler) flowCtime() time.Time { return f.ctime }
2022-11-20 08:49:24 -05:00
2022-11-20 13:42:10 -05:00
func NewFlowHandlerBase(sch Schemer, hand string) FlowHandler {
return FlowHandler{
2022-11-20 12:51:26 -05:00
ID: FlowID(generate.UUID()),
Handler: HandlerKey(hand),
Schema: sch.FlowSchema(),
2022-11-20 08:49:24 -05:00
curStep: StepInit,
ctime: time.Now(),
}
}
func (hk *HandlerKey) String() string {
return string(*hk)
}
2022-11-20 13:42:10 -05:00
func (fm *FlowHandler) Handlers() []*HandlerKey {
2022-11-20 08:49:24 -05:00
return []*HandlerKey{&fm.Handler, nil}
}
func resultErrs(e Errors) Errors {
if e == nil {
return []string{}
}
return e
}
2022-11-20 13:42:10 -05:00
func (fm *FlowHandler) ShowForm(errs Errors) *Result {
2022-11-20 12:51:26 -05:00
res := &Result{
2022-11-20 08:49:24 -05:00
Type: TypeForm,
ID: fm.ID,
StepID: stepPtr(fm.curStep),
Schema: fm.Schema,
Handler: fm.Handlers(),
Errors: resultErrs(errs),
}
return res
}
func (fm *FlowManager) Delete(id FlowID) {
delete(fm.flows, id)
}
const (
2022-11-20 12:51:26 -05:00
TypeForm ResultType = "form"
TypeCreateEntry ResultType = "create_entry"
TypeAbort ResultType = "abort"
TypeExternalStep ResultType = "external"
TypeExternalStepDone ResultType = "external_done"
TypeShowProgress ResultType = "progress"
TypeShowProgressDone ResultType = "progress_done"
TypeMenu ResultType = "menu"
2022-11-20 08:49:24 -05:00
)
2022-11-20 13:42:10 -05:00
func (f *FlowHandler) touch() {
2022-11-20 08:49:24 -05:00
f.ctime = time.Now()
}
func (fm *FlowManager) Register(f Handler) {
fm.flows.cull()
fm.flows[f.FlowID()] = f
}
func (fs *FlowManager) Remove(f Handler) {
delete(fs.flows, f.FlowID())
}
const cullAge = time.Minute * 30
func (fs FlowStore) cull() {
for k, v := range fs {
if time.Now().Sub(v.flowCtime()) > cullAge {
delete(fs, k)
}
}
}
func (fs *FlowManager) Get(id FlowID) Handler {
f, ok := fs.flows[id]
if ok {
return f
}
return nil
}