From b1b21a527b1599fa56831909e10dae951eb382bd Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 6 Jan 2022 18:24:18 +0800 Subject: [PATCH] + Table Hook SearchBefore & SearchAfter ( pre ) --- flow/flow_test.go | 2 +- helper/map.go | 6 ++++ helper/map.process.go | 11 +++++- helper/process.go | 1 + share/api.go | 2 +- table/json.go | 42 +++++++++++++++++++++++ table/process.go | 14 ++++++-- table/process_test.go | 36 +++++++++++++++++-- table/table.go | 34 ++++++++++++++++++ table/table_test.go | 2 +- table/types.go | 27 +++++++++++++++ table/validate.go | 6 ++++ tests/flows/hooks/after_search.flow.json | 13 +++++++ tests/flows/hooks/before_search.flow.json | 7 ++++ tests/tables/hooks/search.tab.json | 39 +++++++++++++++++++++ xlog/xlog.go | 8 ++++- 16 files changed, 241 insertions(+), 9 deletions(-) create mode 100644 table/json.go create mode 100644 table/validate.go create mode 100644 tests/flows/hooks/after_search.flow.json create mode 100644 tests/flows/hooks/before_search.flow.json create mode 100644 tests/tables/hooks/search.tab.json diff --git a/flow/flow_test.go b/flow/flow_test.go index 29be2c33..fcbd66c1 100644 --- a/flow/flow_test.go +++ b/flow/flow_test.go @@ -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)) } diff --git a/helper/map.go b/helper/map.go index 21f6c792..95c50c4d 100644 --- a/helper/map.go +++ b/helper/map.go @@ -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 +} diff --git a/helper/map.process.go b/helper/map.process.go index 9cd36ec8..9ca0e5e7 100644 --- a/helper/map.process.go +++ b/helper/map.process.go @@ -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) +} diff --git a/helper/process.go b/helper/process.go index 10fee0ca..16ffcf59 100644 --- a/helper/process.go +++ b/helper/process.go @@ -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) diff --git a/share/api.go b/share/api.go index dfe5bcae..edc03b06 100644 --- a/share/api.go +++ b/share/api.go @@ -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 } diff --git a/table/json.go b/table/json.go new file mode 100644 index 00000000..4861f272 --- /dev/null +++ b/table/json.go @@ -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) +// } diff --git a/table/process.go b/table/process.go index 5c636ec3..6750756f 100644 --- a/table/process.go +++ b/table/process.go @@ -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 diff --git a/table/process_test.go b/table/process_test.go index a9bcb15c..b6d20a60 100644 --- a/table/process_test.go +++ b/table/process_test.go @@ -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", diff --git a/table/table.go b/table/table.go index 03a1472d..c7c7549a 100644 --- a/table/table.go +++ b/table/table.go @@ -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 == "" { diff --git a/table/table_test.go b/table/table_test.go index 9ccf2389..bbbf86fe 100644 --- a/table/table_test.go +++ b/table/table_test.go @@ -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)) } diff --git a/table/types.go b/table/types.go index 9acc10bd..70647e8d 100644 --- a/table/types.go +++ b/table/types.go @@ -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"` +} diff --git a/table/validate.go b/table/validate.go new file mode 100644 index 00000000..01df3881 --- /dev/null +++ b/table/validate.go @@ -0,0 +1,6 @@ +package table + +// Validate 校验表格格式 +// func (table Table) Validate() error { +// return nil +// } diff --git a/tests/flows/hooks/after_search.flow.json b/tests/flows/hooks/after_search.flow.json new file mode 100644 index 00000000..eb47c663 --- /dev/null +++ b/tests/flows/hooks/after_search.flow.json @@ -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.结果}}" +} diff --git a/tests/flows/hooks/before_search.flow.json b/tests/flows/hooks/before_search.flow.json new file mode 100644 index 00000000..6567dff0 --- /dev/null +++ b/tests/flows/hooks/before_search.flow.json @@ -0,0 +1,7 @@ +{ + "label": "Before:Search", + "version": "1.0.0", + "description": "Before:Search", + "nodes": [], + "output": [{ "wheres": [{ "column": "status", "value": "enabled" }] }, 1, 2] +} diff --git a/tests/tables/hooks/search.tab.json b/tests/tables/hooks/search.tab.json new file mode 100644 index 00000000..d38f27b5 --- /dev/null +++ b/tests/tables/hooks/search.tab.json @@ -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": {} +} diff --git a/xlog/xlog.go b/xlog/xlog.go index 1b6b1a3b..49848570 100644 --- a/xlog/xlog.go +++ b/xlog/xlog.go @@ -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).