+ Table Hook SearchBefore & SearchAfter ( pre )
This commit is contained in:
parent
0c07b6ea2b
commit
b1b21a527b
16 changed files with 241 additions and 9 deletions
|
|
@ -20,5 +20,5 @@ func check(t *testing.T) {
|
|||
for key := range gou.Flows {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
assert.Equal(t, 10, len(keys))
|
||||
assert.Equal(t, 12, len(keys))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,3 +25,9 @@ func MapGet(record map[string]interface{}, key string) interface{} {
|
|||
data := maps.MapOf(record).Dot()
|
||||
return data.Get(key)
|
||||
}
|
||||
|
||||
// MapSet xiang.helper.MapSet 设定数值并返回新映射表
|
||||
func MapSet(record map[string]interface{}, key string, value interface{}) map[string]interface{} {
|
||||
record[key] = value
|
||||
return record
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,19 @@ func ProcessMapKeys(process *gou.Process) interface{} {
|
|||
return MapKeys(record)
|
||||
}
|
||||
|
||||
// ProcessMapGet xiang.helper.MapKey 返回映射表给定键的值
|
||||
// ProcessMapGet xiang.helper.MapGet 返回映射表给定键的值
|
||||
func ProcessMapGet(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
record := process.ArgsMap(0)
|
||||
key := process.ArgsString(1)
|
||||
return MapGet(record, key)
|
||||
}
|
||||
|
||||
// ProcessMapSet xiang.helper.MapSet 返回映射表给定键的值
|
||||
func ProcessMapSet(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(3)
|
||||
record := process.ArgsMap(0)
|
||||
key := process.ArgsString(1)
|
||||
value := process.Args[2]
|
||||
return MapSet(record, key, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ func init() {
|
|||
gou.RegisterProcessHandler("xiang.helper.MapKeys", ProcessMapKeys)
|
||||
gou.RegisterProcessHandler("xiang.helper.MapValues", ProcessMapValues)
|
||||
gou.RegisterProcessHandler("xiang.helper.MapGet", ProcessMapGet)
|
||||
gou.RegisterProcessHandler("xiang.helper.MapSet", ProcessMapSet)
|
||||
|
||||
gou.RegisterProcessHandler("xiang.helper.Captcha", ProcessCaptcha)
|
||||
gou.RegisterProcessHandler("xiang.helper.CaptchaValidate", ProcessCaptchaValidate)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ func (api API) DefaultInt(i int, defaults ...int) int {
|
|||
value = defaults[0]
|
||||
}
|
||||
|
||||
if api.Default[i] == nil || len(api.Default) <= i {
|
||||
if len(api.Default) <= i || api.Default[i] == nil {
|
||||
return value
|
||||
}
|
||||
|
||||
|
|
|
|||
42
table/json.go
Normal file
42
table/json.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package table
|
||||
|
||||
// // UnmarshalJSON for json marshalJSON
|
||||
// func (table *Table) UnmarshalJSON(data []byte) error {
|
||||
|
||||
// var v interface{}
|
||||
// err := jsoniter.Unmarshal(data, &v)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// // values := []interface{}{}
|
||||
// // switch v.(type) {
|
||||
// // case string: // "kind rollup 所有类型, city"
|
||||
// // strarr := strings.Split(v.(string), ",")
|
||||
// // for _, str := range strarr {
|
||||
// // groups.PushString(str)
|
||||
// // }
|
||||
// // break
|
||||
// // case []interface{}: // ["name", {"field":"foo"}, "id rollup 所有类型"]
|
||||
// // values = v.([]interface{})
|
||||
// // break
|
||||
// // }
|
||||
|
||||
// // for _, value := range values {
|
||||
// // switch value.(type) {
|
||||
// // case string: // "kind rollup 所有类型"
|
||||
// // groups.PushString(value.(string))
|
||||
// // break
|
||||
// // case map[string]interface{}: // {"field":"foo"}
|
||||
// // groups.PushMap(value.(map[string]interface{}))
|
||||
// // break
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // MarshalJSON for json marshalJSON
|
||||
// func (table Table) MarshalJSON() ([]byte, error) {
|
||||
// return jsoniter.Marshal(nil)
|
||||
// }
|
||||
|
|
@ -28,20 +28,30 @@ func init() {
|
|||
// 按条件查询数据记录, 请求成功返回符合查询条件带有分页信息的数据对象
|
||||
func ProcessSearch(process *gou.Process) interface{} {
|
||||
|
||||
process.ValidateArgNums(4)
|
||||
// 读取表格名称
|
||||
process.ValidateArgNums(1)
|
||||
name := process.ArgsString(0)
|
||||
table := Select(name)
|
||||
|
||||
api := table.APIs["search"].ValidateLoop("xiang.table.search")
|
||||
if process.NumOfArgsIs(5) && api.IsAllow(process.Args[4]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Before Hook
|
||||
process.Args = table.Before(table.Hooks.BeforeSearch, process.Args)
|
||||
|
||||
// 参数表
|
||||
process.ValidateArgNums(4)
|
||||
param := api.MergeDefaultQueryParam(process.ArgsQueryParams(1), 0)
|
||||
page := process.ArgsInt(2, api.DefaultInt(1))
|
||||
pagesize := process.ArgsInt(3, api.DefaultInt(2))
|
||||
|
||||
return gou.NewProcess(api.Process, param, page, pagesize).Run()
|
||||
// 查询数据
|
||||
response := gou.NewProcess(api.Process, param, page, pagesize).Run()
|
||||
|
||||
// After Hook
|
||||
return table.After(table.Hooks.AfterSearch, response)
|
||||
}
|
||||
|
||||
// ProcessFind xiang.table.Find
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package table
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
|
@ -8,8 +10,12 @@ import (
|
|||
"github.com/yaoapp/gou"
|
||||
"github.com/yaoapp/kun/any"
|
||||
"github.com/yaoapp/kun/maps"
|
||||
"github.com/yaoapp/kun/utils"
|
||||
"github.com/yaoapp/xiang/config"
|
||||
"github.com/yaoapp/xiang/flow"
|
||||
_ "github.com/yaoapp/xiang/helper"
|
||||
"github.com/yaoapp/xiang/model"
|
||||
"github.com/yaoapp/xiang/query"
|
||||
"github.com/yaoapp/xiang/share"
|
||||
"github.com/yaoapp/xun/capsule"
|
||||
)
|
||||
|
|
@ -18,7 +24,14 @@ func init() {
|
|||
share.DBConnect(config.Conf.Database)
|
||||
model.Load(config.Conf)
|
||||
share.Load(config.Conf)
|
||||
query.Load(config.Conf)
|
||||
flow.LoadFrom(filepath.Join(config.Conf.Root, "flows", "hooks"), "hooks.")
|
||||
Load(config.Conf)
|
||||
|
||||
for name := range gou.Flows {
|
||||
fmt.Println(name)
|
||||
}
|
||||
fmt.Println(filepath.Join(config.Conf.Root, "tables", "hooks"))
|
||||
}
|
||||
func TestTableProcessSearch(t *testing.T) {
|
||||
|
||||
|
|
@ -33,8 +46,7 @@ func TestTableProcessSearch(t *testing.T) {
|
|||
2,
|
||||
&gin.Context{},
|
||||
}
|
||||
process := gou.NewProcess("xiang.table.Search", args...)
|
||||
response := ProcessSearch(process)
|
||||
response := gou.NewProcess("xiang.table.Search", args...).Run()
|
||||
assert.NotNil(t, response)
|
||||
res := any.Of(response).Map()
|
||||
assert.True(t, res.Has("data"))
|
||||
|
|
@ -48,6 +60,26 @@ func TestTableProcessSearch(t *testing.T) {
|
|||
assert.Equal(t, 2, res.Get("pagesize"))
|
||||
}
|
||||
|
||||
func TestTableProcessSearchWithHook(t *testing.T) {
|
||||
|
||||
args := []interface{}{"hooks.search"}
|
||||
response := gou.NewProcess("xiang.table.Search", args...).Run()
|
||||
utils.Dump(response)
|
||||
|
||||
assert.NotNil(t, response)
|
||||
res := any.Of(response).Map()
|
||||
assert.True(t, res.Has("data"))
|
||||
assert.True(t, res.Has("next"))
|
||||
assert.True(t, res.Has("page"))
|
||||
assert.True(t, res.Has("pagecnt"))
|
||||
assert.True(t, res.Has("pagesize"))
|
||||
assert.True(t, res.Has("prev"))
|
||||
assert.True(t, res.Has("total"))
|
||||
assert.Equal(t, 1, res.Get("page"))
|
||||
assert.Equal(t, 2, res.Get("pagesize"))
|
||||
assert.Equal(t, float64(100), res.Get("after"))
|
||||
}
|
||||
|
||||
func TestTableProcessFind(t *testing.T) {
|
||||
args := []interface{}{
|
||||
"service",
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ import (
|
|||
"github.com/yaoapp/gou"
|
||||
"github.com/yaoapp/gou/helper"
|
||||
"github.com/yaoapp/kun/exception"
|
||||
"github.com/yaoapp/kun/maps"
|
||||
"github.com/yaoapp/xiang/config"
|
||||
"github.com/yaoapp/xiang/share"
|
||||
"github.com/yaoapp/xiang/xlog"
|
||||
)
|
||||
|
||||
// Tables 已载入模型
|
||||
|
|
@ -147,6 +149,38 @@ func getDefaultAPIs(bind Bind) map[string]share.API {
|
|||
return apis
|
||||
}
|
||||
|
||||
// Before 运行 Before hook
|
||||
func (table *Table) Before(process string, processArgs []interface{}) []interface{} {
|
||||
if process == "" {
|
||||
return processArgs
|
||||
}
|
||||
args := []interface{}{}
|
||||
res := []interface{}{}
|
||||
if len(processArgs) > 0 {
|
||||
args = processArgs[1:]
|
||||
res = append(res, processArgs[0])
|
||||
}
|
||||
|
||||
response := gou.NewProcess(process, args...).Run()
|
||||
if fixedArgs, ok := response.([]interface{}); ok {
|
||||
res = append(res, fixedArgs...)
|
||||
return res
|
||||
}
|
||||
|
||||
xlog.Println("无效的处理器", maps.StrAny{"process": process, "response": response})
|
||||
return processArgs
|
||||
}
|
||||
|
||||
// After 运行 After hook
|
||||
func (table *Table) After(process string, data interface{}) interface{} {
|
||||
if process == "" {
|
||||
return data
|
||||
}
|
||||
|
||||
fmt.Println("After", process)
|
||||
return gou.NewProcess(process, data).Run()
|
||||
}
|
||||
|
||||
// loadFilters 加载查询过滤器
|
||||
func (table *Table) loadFilters() {
|
||||
if table.Bind.Model == "" {
|
||||
|
|
|
|||
|
|
@ -48,5 +48,5 @@ func check(t *testing.T) {
|
|||
for key := range Tables {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
assert.Equal(t, 4, len(keys))
|
||||
assert.Equal(t, 5, len(keys))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ type Table struct {
|
|||
Title string `json:"title,omitempty"`
|
||||
Decription string `json:"decription,omitempty"`
|
||||
Bind Bind `json:"bind,omitempty"`
|
||||
Hooks Hooks `json:"hooks,omitempty"`
|
||||
APIs map[string]share.API `json:"apis,omitempty"`
|
||||
Columns map[string]share.Column `json:"columns,omitempty"`
|
||||
Filters map[string]share.Filter `json:"filters,omitempty"`
|
||||
|
|
@ -28,3 +29,29 @@ type Bind struct {
|
|||
Model string `json:"model"`
|
||||
Withs map[string]gou.With `json:"withs,omitempty"`
|
||||
}
|
||||
|
||||
// Hooks 表格数据模型
|
||||
type Hooks struct {
|
||||
BeforeFind string `json:"before:find,omitempty"`
|
||||
AfterFind string `json:"after:find,omitempty"`
|
||||
BeforeSearch string `json:"before:search,omitempty"`
|
||||
AfterSearch string `json:"after:search,omitempty"`
|
||||
BeforeSave string `json:"before:save,omitempty"`
|
||||
AfterSave string `json:"after:save,omitempty"`
|
||||
BeforeDelete string `json:"before:delete,omitempty"`
|
||||
AfterDelete string `json:"after:delete,omitempty"`
|
||||
BeforeInsert string `json:"before:insert,omitempty"`
|
||||
AfterInsert string `json:"after:insert,omitempty"`
|
||||
BeforeDeleteIn string `json:"before:delete-in,omitempty"`
|
||||
AfterDeleteIn string `json:"after:delete-in,omitempty"`
|
||||
BeforeDeleteWhere string `json:"before:delete-where,omitempty"`
|
||||
AfterDeleteWhere string `json:"after:delete-where,omitempty"`
|
||||
BeforeUpdateIn string `json:"before:update-in,omitempty"`
|
||||
AfterUpdateIn string `json:"after:update-in,omitempty"`
|
||||
BeforeUpdateWhere string `json:"before:update-where,omitempty"`
|
||||
AfterUpdateWhere string `json:"after:update-where,omitempty"`
|
||||
BeforeQuicksave string `json:"before:quicksave,omitempty"`
|
||||
AfterQuicksave string `json:"after:quicksave,omitempty"`
|
||||
BeforeSelect string `json:"before:select,omitempty"`
|
||||
AfterSelect string `json:"after:select,omitempty"`
|
||||
}
|
||||
|
|
|
|||
6
table/validate.go
Normal file
6
table/validate.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package table
|
||||
|
||||
// Validate 校验表格格式
|
||||
// func (table Table) Validate() error {
|
||||
// return nil
|
||||
// }
|
||||
13
tests/flows/hooks/after_search.flow.json
Normal file
13
tests/flows/hooks/after_search.flow.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"label": "After:Search",
|
||||
"version": "1.0.0",
|
||||
"description": "After:Search",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "结果",
|
||||
"process": "xiang.helper.MapSet",
|
||||
"args": ["{{$in.0}}", "after", 100]
|
||||
}
|
||||
],
|
||||
"output": "{{$res.结果}}"
|
||||
}
|
||||
7
tests/flows/hooks/before_search.flow.json
Normal file
7
tests/flows/hooks/before_search.flow.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"label": "Before:Search",
|
||||
"version": "1.0.0",
|
||||
"description": "Before:Search",
|
||||
"nodes": [],
|
||||
"output": [{ "wheres": [{ "column": "status", "value": "enabled" }] }, 1, 2]
|
||||
}
|
||||
39
tests/tables/hooks/search.tab.json
Normal file
39
tests/tables/hooks/search.tab.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"name": "云服务库",
|
||||
"version": "1.0.0",
|
||||
"decription": "云服务库",
|
||||
"bind": {
|
||||
"model": "service",
|
||||
"withs": {}
|
||||
},
|
||||
"hooks": {
|
||||
"before:search": "flows.hooks.before_search",
|
||||
"after:search": "flows.hooks.after_search"
|
||||
},
|
||||
"apis": {
|
||||
"search": {
|
||||
"process": "models.service.Paginate",
|
||||
"guard": "-",
|
||||
"default": [null, null, 15]
|
||||
}
|
||||
},
|
||||
"columns": {},
|
||||
"filters": {},
|
||||
"list": {
|
||||
"primary": "id",
|
||||
"layout": {
|
||||
"columns": [{ "name": "ID", "width": 6 }],
|
||||
"filters": []
|
||||
},
|
||||
"actions": {}
|
||||
},
|
||||
"edit": {
|
||||
"primary": "id",
|
||||
"layout": {
|
||||
"fieldset": [{ "columns": [{ "name": "ID", "width": 6 }] }]
|
||||
},
|
||||
"actions": { "cancel": {} }
|
||||
},
|
||||
"insert": {},
|
||||
"view": {}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ package xlog
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
// XLog 日志接口
|
||||
|
|
@ -25,7 +27,11 @@ func Printf(format string, v ...interface{}) {
|
|||
// Println calls Output to print to the standard logger.
|
||||
// Arguments are handled in the manner of fmt.Println.
|
||||
func Println(v ...interface{}) {
|
||||
log.Output(2, fmt.Sprintln(v...))
|
||||
content, err := jsoniter.Marshal(v)
|
||||
if err != nil {
|
||||
log.Output(2, fmt.Sprintln(v...))
|
||||
}
|
||||
log.Output(2, string(content))
|
||||
}
|
||||
|
||||
// Fatalf is equivalent to Printf() followed by a call to os.Exit(1).
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue