201 lines
4.2 KiB
Go
201 lines
4.2 KiB
Go
// flow is the data entry flow.
|
|
package flow
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"dynatron.me/x/blasphem/internal/generate"
|
|
)
|
|
|
|
type (
|
|
ResultType string
|
|
FlowID string
|
|
Step string
|
|
HandlerKey string
|
|
Errors interface{}
|
|
|
|
Context interface{}
|
|
|
|
FlowStore map[FlowID]Handler
|
|
|
|
FlowManager struct {
|
|
flows FlowStore
|
|
}
|
|
|
|
Result struct {
|
|
Type ResultType `json:"type"`
|
|
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"`
|
|
Schema []SchemaItem `json:"data_schema"`
|
|
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"`
|
|
}
|
|
|
|
SchemaItem struct {
|
|
Type string `json:"type"`
|
|
Name string `json:"name"`
|
|
Required bool `json:"required"`
|
|
}
|
|
|
|
Schema []SchemaItem
|
|
)
|
|
|
|
type (
|
|
Schemer interface {
|
|
FlowSchema() Schema
|
|
}
|
|
|
|
Handler interface {
|
|
BaseHandler() FlowHandler
|
|
FlowID() FlowID
|
|
|
|
flowCtime() time.Time
|
|
}
|
|
)
|
|
|
|
const (
|
|
StepInit Step = "init"
|
|
)
|
|
|
|
func (fs *Schema) CheckRequired(rm map[string]interface{}) error {
|
|
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 }
|
|
|
|
type FlowHandler struct {
|
|
ID FlowID // ID is the FlowID
|
|
Handler HandlerKey // Handler key
|
|
Context Context // flow Context
|
|
Schema Schema
|
|
|
|
// curStep is the current step set by the flow manager
|
|
curStep Step
|
|
|
|
ctime time.Time
|
|
}
|
|
|
|
func (f *FlowHandler) Step() Step { return f.curStep }
|
|
|
|
func (f *FlowHandler) BaseHandler() FlowHandler { return *f }
|
|
|
|
func (f *FlowHandler) FlowID() FlowID {
|
|
return f.ID
|
|
}
|
|
|
|
func (f *FlowHandler) flowCtime() time.Time { return f.ctime }
|
|
|
|
func NewFlowHandlerBase(sch Schemer, hand string) FlowHandler {
|
|
return FlowHandler{
|
|
ID: FlowID(generate.UUID()),
|
|
Handler: HandlerKey(hand),
|
|
Schema: sch.FlowSchema(),
|
|
|
|
curStep: StepInit,
|
|
ctime: time.Now(),
|
|
}
|
|
}
|
|
|
|
func (hk *HandlerKey) String() string {
|
|
return string(*hk)
|
|
}
|
|
|
|
func (fm *FlowHandler) Handlers() []*HandlerKey {
|
|
return []*HandlerKey{&fm.Handler, nil}
|
|
}
|
|
|
|
func resultErrs(e Errors) Errors {
|
|
if e == nil {
|
|
return []string{}
|
|
}
|
|
|
|
return e
|
|
}
|
|
|
|
func (fm *FlowHandler) ShowForm(errs Errors) *Result {
|
|
res := &Result{
|
|
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 (
|
|
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"
|
|
)
|
|
|
|
func (f *FlowHandler) touch() {
|
|
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 * 10
|
|
|
|
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
|
|
}
|