yao/widgets/table/action.go
2022-09-24 22:37:18 +08:00

359 lines
9.6 KiB
Go

package table
import (
"fmt"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/any"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/kun/maps"
)
var processActionDefaults = map[string]*ProcessActionDSL{
"Setting": {
Name: "yao.table.Setting",
Process: "yao.table.Xgen",
Default: []interface{}{},
},
"Component": {
Name: "yao.table.Component",
Default: []interface{}{nil, nil, nil},
},
"Search": {
Name: "yao.table.Search",
Default: []interface{}{nil, 1, 20},
},
"Get": {
Name: "yao.table.Get",
Default: []interface{}{nil},
},
"Find": {
Name: "yao.table.Find",
Default: []interface{}{nil, nil},
},
"Save": {
Name: "yao.table.Save",
Default: []interface{}{nil},
},
"Create": {
Name: "yao.table.Create",
Default: []interface{}{nil},
},
"Insert": {
Name: "yao.table.Insert",
Default: []interface{}{nil, nil},
},
"Update": {
Name: "yao.table.Update",
Default: []interface{}{nil, nil},
},
"UpdateWhere": {
Name: "yao.table.UpdateWhere",
Default: []interface{}{nil, nil},
},
"UpdateIn": {
Name: "yao.table.UpdateIn",
Default: []interface{}{nil, nil},
},
"Delete": {
Name: "yao.table.Delete",
Default: []interface{}{nil},
},
"DeleteWhere": {
Name: "yao.table.DeleteWhere",
Default: []interface{}{nil},
},
"DeleteIn": {
Name: "yao.table.DeleteIn",
Default: []interface{}{nil},
},
}
// SetDefaultProcess set the default value of action
func (action *ActionDSL) SetDefaultProcess() {
action.Setting = action.newProcessAction("Setting", action.Setting, nil, nil)
action.Component = action.newProcessAction("Component", action.Component, nil, nil)
action.Search = action.newProcessAction("Search", action.Search, action.BeforeSearch, action.AfterSearch)
action.Get = action.newProcessAction("Get", action.Get, action.BeforeGet, action.AfterGet)
action.Find = action.newProcessAction("Find", action.Find, action.BeforeFind, action.AfterFind)
action.Save = action.newProcessAction("Save", action.Save, action.BeforeSave, action.AfterSave)
action.Create = action.newProcessAction("Create", action.Create, action.BeforeCreate, action.AfterCreate)
action.Insert = action.newProcessAction("Insert", action.Insert, action.BeforeInsert, action.AfterInsert)
action.Update = action.newProcessAction("Update", action.Update, action.BeforeUpdate, action.AfterUpdate)
action.UpdateWhere = action.newProcessAction("UpdateWhere", action.UpdateWhere, action.BeforeUpdateWhere, action.AfterUpdateWhere)
action.UpdateIn = action.newProcessAction("UpdateIn", action.UpdateIn, action.BeforeUpdateIn, action.AfterUpdateIn)
action.Delete = action.newProcessAction("Delete", action.Delete, action.BeforeDelete, action.AfterDelete)
action.DeleteWhere = action.newProcessAction("DeleteWhere", action.DeleteWhere, action.BeforeDeleteWhere, action.AfterDeleteWhere)
action.DeleteIn = action.newProcessAction("DeleteIn", action.DeleteIn, action.BeforeDeleteIn, action.AfterDeleteIn)
}
// BindModel bind model
func (action *ActionDSL) BindModel(m *gou.Model) {
name := m.ID // should be id
action.Search.Bind(fmt.Sprintf("models.%s.Paginate", name))
action.Get.Bind(fmt.Sprintf("models.%s.Get", name))
action.Find.Bind(fmt.Sprintf("models.%s.Find", name))
action.Save.Bind(fmt.Sprintf("models.%s.Save", name))
action.Create.Bind(fmt.Sprintf("models.%s.Create", name))
action.Insert.Bind(fmt.Sprintf("models.%s.Insert", name))
action.Update.Bind(fmt.Sprintf("models.%s.Update", name))
action.UpdateWhere.Bind(fmt.Sprintf("models.%s.UpdateWhere", name))
action.UpdateIn.Bind(fmt.Sprintf("models.%s.UpdateWhere", name))
action.Delete.Bind(fmt.Sprintf("models.%s.Delete", name))
action.DeleteWhere.Bind(fmt.Sprintf("models.%s.DeleteWhere", name))
action.DeleteIn.Bind(fmt.Sprintf("models.%s.DeleteWhere", name))
// bind options
if action.Bind.Option != nil {
action.Search.Default[0] = action.Bind.Option
action.Get.Default[0] = action.Bind.Option
action.Find.Default[1] = action.Bind.Option
}
}
// setDefault Set the process action disabled
func (action *ActionDSL) newProcessAction(name string, p *ProcessActionDSL, before *BeforeHookActionDSL, after *AfterHookActionDSL) *ProcessActionDSL {
if p == nil {
p = &ProcessActionDSL{}
}
p.After = after
p.Before = before
if defaultProcess, has := processActionDefaults[name]; has {
p.Name = defaultProcess.Name
if p.Process == "" {
p.Process = defaultProcess.Process
}
if p.Guard == "" {
p.Guard = defaultProcess.Guard
}
if p.Default == nil {
p.Default = defaultProcess.Default
}
// format defaults
if len(p.Default) != len(defaultProcess.Default) {
defauts := defaultProcess.Default
nums := len(p.Default)
if nums > len(defaultProcess.Default) {
nums = len(defaultProcess.Default)
}
for i := 0; i < nums; i++ {
defauts[i] = p.Default[i]
}
p.Default = defauts
}
}
return p
}
// Bind the process name
func (p *ProcessActionDSL) Bind(name string) {
p.ProcessBind = name
}
// Args get the process args
func (p *ProcessActionDSL) Args(process *gou.Process) []interface{} {
process.ValidateArgNums(1)
args := p.Default
nums := len(process.Args[1:])
if nums > len(args) {
nums = len(args)
}
for i := 0; i < nums; i++ {
args[i] = p.deepMergeDefault(process.Args[i+1], args[i])
}
return args
}
// Exec exec the process
func (p *ProcessActionDSL) Exec(process *gou.Process) (interface{}, error) {
tab, err := Get(process)
if err != nil {
return nil, err
}
args := p.Args(process)
// Process
name := p.Process
if name == "" {
name = p.ProcessBind
}
if name == "" {
log.Error("[table] %s %s process is required", tab.ID, p.Name)
return nil, fmt.Errorf("[table] %s %s process is required", tab.ID, p.Name)
}
// Before Hook
if p.Before != nil {
log.Trace("[table] %s %s before: exec(%v)", tab.ID, p.Name, args)
newArgs, err := p.Before.Exec(args, process.Sid, process.Global)
if err != nil {
log.Error("[table] %s %s before: %s", tab.ID, p.Name, err.Error())
} else {
log.Trace("[table] %s %s before: args:%v", tab.ID, p.Name, args)
args = newArgs
}
}
// Compute In
switch p.Name {
case "yao.table.Save", "yao.table.Create":
switch args[0].(type) {
case map[string]interface{}:
data := args[0].(map[string]interface{})
err := tab.computeSave(process, data)
if err != nil {
log.Error("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error())
}
args[0] = data
}
break
case "yao.table.Update", "yao.table.UpdateWhere", "yao.table.UpdateIn":
switch args[1].(type) {
case map[string]interface{}:
data := args[1].(map[string]interface{})
err := tab.computeSave(process, data)
if err != nil {
log.Error("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error())
}
args[1] = data
}
break
case "yao.table.Insert":
break
}
// Execute Process
act, err := gou.ProcessOf(name, args...)
if err != nil {
log.Error("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error())
return nil, fmt.Errorf("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error())
}
res, err := act.WithGlobal(process.Global).WithSID(process.Sid).Exec()
if err != nil {
log.Error("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error())
return nil, fmt.Errorf("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error())
}
// Compute Out
switch p.Name {
case "yao.table.Search":
if newMap, ok := res.(map[string]interface{}); ok {
err := tab.computeSearch(process, newMap, "data")
if err != nil {
log.Error("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error())
}
res = newMap
}
break
case "yao.table.Get":
if _, ok := res.([]maps.MapStr); ok {
data := []interface{}{}
for _, v := range res.([]maps.MapStr) {
data = append(data, map[string]interface{}(v))
}
res = data
}
if data, ok := res.([]interface{}); ok {
err := tab.computeGet(process, data)
if err != nil {
log.Error("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error())
}
res = data
}
break
case "yao.table.Find":
switch res.(type) {
case map[string]interface{}, maps.MapStr:
data := any.MapOf(res).MapStrAny
err := tab.computeFind(process, data)
if err != nil {
log.Error("[table] %s %s -> %s %s", tab.ID, p.Name, name, err.Error())
}
res = data
}
break
}
// After hook
if p.After != nil {
log.Trace("[table] %s %s after: exec(%v)", tab.ID, p.Name, res)
newRes, err := p.After.Exec(res, process.Sid, process.Global)
if err != nil {
log.Error("[table] %s %s after: %s", tab.ID, p.Name, err.Error())
} else {
log.Trace("[table] %s %s after: %v", tab.ID, p.Name, newRes)
res = newRes
}
}
return res, nil
}
// MustExec exec the process
func (p *ProcessActionDSL) MustExec(process *gou.Process) interface{} {
res, err := p.Exec(process)
if err != nil {
exception.New(err.Error(), 500).Throw()
}
return res
}
// deepMergeDefault deep merge args
func (p *ProcessActionDSL) deepMergeDefault(value interface{}, defaults interface{}) interface{} {
if value == nil {
return defaults
}
switch defaults.(type) {
case map[string]interface{}:
defaultMap := defaults.(map[string]interface{})
valueMap := any.Of(value).MapStr()
for key, v := range defaultMap {
valueMap[key] = p.deepMergeDefault(valueMap[key], v)
}
return valueMap
case []interface{}:
defaultArr := defaults.([]interface{})
valueArr := any.Of(value).CArray()
// pad
nums := len(defaultArr) - len(valueArr)
for i := 0; i < nums; i++ {
valueArr = append(valueArr, nil)
}
// set default
for idx, v := range defaultArr {
valueArr[idx] = p.deepMergeDefault(valueArr[idx], v)
}
return valueArr
}
return value
}