+ 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 {
|
for key := range gou.Flows {
|
||||||
keys = append(keys, key)
|
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()
|
data := maps.MapOf(record).Dot()
|
||||||
return data.Get(key)
|
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)
|
return MapKeys(record)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessMapGet xiang.helper.MapKey 返回映射表给定键的值
|
// ProcessMapGet xiang.helper.MapGet 返回映射表给定键的值
|
||||||
func ProcessMapGet(process *gou.Process) interface{} {
|
func ProcessMapGet(process *gou.Process) interface{} {
|
||||||
process.ValidateArgNums(2)
|
process.ValidateArgNums(2)
|
||||||
record := process.ArgsMap(0)
|
record := process.ArgsMap(0)
|
||||||
key := process.ArgsString(1)
|
key := process.ArgsString(1)
|
||||||
return MapGet(record, key)
|
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.MapKeys", ProcessMapKeys)
|
||||||
gou.RegisterProcessHandler("xiang.helper.MapValues", ProcessMapValues)
|
gou.RegisterProcessHandler("xiang.helper.MapValues", ProcessMapValues)
|
||||||
gou.RegisterProcessHandler("xiang.helper.MapGet", ProcessMapGet)
|
gou.RegisterProcessHandler("xiang.helper.MapGet", ProcessMapGet)
|
||||||
|
gou.RegisterProcessHandler("xiang.helper.MapSet", ProcessMapSet)
|
||||||
|
|
||||||
gou.RegisterProcessHandler("xiang.helper.Captcha", ProcessCaptcha)
|
gou.RegisterProcessHandler("xiang.helper.Captcha", ProcessCaptcha)
|
||||||
gou.RegisterProcessHandler("xiang.helper.CaptchaValidate", ProcessCaptchaValidate)
|
gou.RegisterProcessHandler("xiang.helper.CaptchaValidate", ProcessCaptchaValidate)
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ func (api API) DefaultInt(i int, defaults ...int) int {
|
||||||
value = defaults[0]
|
value = defaults[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if api.Default[i] == nil || len(api.Default) <= i {
|
if len(api.Default) <= i || api.Default[i] == nil {
|
||||||
return value
|
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{} {
|
func ProcessSearch(process *gou.Process) interface{} {
|
||||||
|
|
||||||
process.ValidateArgNums(4)
|
// 读取表格名称
|
||||||
|
process.ValidateArgNums(1)
|
||||||
name := process.ArgsString(0)
|
name := process.ArgsString(0)
|
||||||
table := Select(name)
|
table := Select(name)
|
||||||
|
|
||||||
api := table.APIs["search"].ValidateLoop("xiang.table.search")
|
api := table.APIs["search"].ValidateLoop("xiang.table.search")
|
||||||
if process.NumOfArgsIs(5) && api.IsAllow(process.Args[4]) {
|
if process.NumOfArgsIs(5) && api.IsAllow(process.Args[4]) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Before Hook
|
||||||
|
process.Args = table.Before(table.Hooks.BeforeSearch, process.Args)
|
||||||
|
|
||||||
// 参数表
|
// 参数表
|
||||||
|
process.ValidateArgNums(4)
|
||||||
param := api.MergeDefaultQueryParam(process.ArgsQueryParams(1), 0)
|
param := api.MergeDefaultQueryParam(process.ArgsQueryParams(1), 0)
|
||||||
page := process.ArgsInt(2, api.DefaultInt(1))
|
page := process.ArgsInt(2, api.DefaultInt(1))
|
||||||
pagesize := process.ArgsInt(3, api.DefaultInt(2))
|
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
|
// ProcessFind xiang.table.Find
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package table
|
package table
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
@ -8,8 +10,12 @@ import (
|
||||||
"github.com/yaoapp/gou"
|
"github.com/yaoapp/gou"
|
||||||
"github.com/yaoapp/kun/any"
|
"github.com/yaoapp/kun/any"
|
||||||
"github.com/yaoapp/kun/maps"
|
"github.com/yaoapp/kun/maps"
|
||||||
|
"github.com/yaoapp/kun/utils"
|
||||||
"github.com/yaoapp/xiang/config"
|
"github.com/yaoapp/xiang/config"
|
||||||
|
"github.com/yaoapp/xiang/flow"
|
||||||
|
_ "github.com/yaoapp/xiang/helper"
|
||||||
"github.com/yaoapp/xiang/model"
|
"github.com/yaoapp/xiang/model"
|
||||||
|
"github.com/yaoapp/xiang/query"
|
||||||
"github.com/yaoapp/xiang/share"
|
"github.com/yaoapp/xiang/share"
|
||||||
"github.com/yaoapp/xun/capsule"
|
"github.com/yaoapp/xun/capsule"
|
||||||
)
|
)
|
||||||
|
|
@ -18,7 +24,14 @@ func init() {
|
||||||
share.DBConnect(config.Conf.Database)
|
share.DBConnect(config.Conf.Database)
|
||||||
model.Load(config.Conf)
|
model.Load(config.Conf)
|
||||||
share.Load(config.Conf)
|
share.Load(config.Conf)
|
||||||
|
query.Load(config.Conf)
|
||||||
|
flow.LoadFrom(filepath.Join(config.Conf.Root, "flows", "hooks"), "hooks.")
|
||||||
Load(config.Conf)
|
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) {
|
func TestTableProcessSearch(t *testing.T) {
|
||||||
|
|
||||||
|
|
@ -33,8 +46,7 @@ func TestTableProcessSearch(t *testing.T) {
|
||||||
2,
|
2,
|
||||||
&gin.Context{},
|
&gin.Context{},
|
||||||
}
|
}
|
||||||
process := gou.NewProcess("xiang.table.Search", args...)
|
response := gou.NewProcess("xiang.table.Search", args...).Run()
|
||||||
response := ProcessSearch(process)
|
|
||||||
assert.NotNil(t, response)
|
assert.NotNil(t, response)
|
||||||
res := any.Of(response).Map()
|
res := any.Of(response).Map()
|
||||||
assert.True(t, res.Has("data"))
|
assert.True(t, res.Has("data"))
|
||||||
|
|
@ -48,6 +60,26 @@ func TestTableProcessSearch(t *testing.T) {
|
||||||
assert.Equal(t, 2, res.Get("pagesize"))
|
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) {
|
func TestTableProcessFind(t *testing.T) {
|
||||||
args := []interface{}{
|
args := []interface{}{
|
||||||
"service",
|
"service",
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,10 @@ import (
|
||||||
"github.com/yaoapp/gou"
|
"github.com/yaoapp/gou"
|
||||||
"github.com/yaoapp/gou/helper"
|
"github.com/yaoapp/gou/helper"
|
||||||
"github.com/yaoapp/kun/exception"
|
"github.com/yaoapp/kun/exception"
|
||||||
|
"github.com/yaoapp/kun/maps"
|
||||||
"github.com/yaoapp/xiang/config"
|
"github.com/yaoapp/xiang/config"
|
||||||
"github.com/yaoapp/xiang/share"
|
"github.com/yaoapp/xiang/share"
|
||||||
|
"github.com/yaoapp/xiang/xlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tables 已载入模型
|
// Tables 已载入模型
|
||||||
|
|
@ -147,6 +149,38 @@ func getDefaultAPIs(bind Bind) map[string]share.API {
|
||||||
return apis
|
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 加载查询过滤器
|
// loadFilters 加载查询过滤器
|
||||||
func (table *Table) loadFilters() {
|
func (table *Table) loadFilters() {
|
||||||
if table.Bind.Model == "" {
|
if table.Bind.Model == "" {
|
||||||
|
|
|
||||||
|
|
@ -48,5 +48,5 @@ func check(t *testing.T) {
|
||||||
for key := range Tables {
|
for key := range Tables {
|
||||||
keys = append(keys, key)
|
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"`
|
Title string `json:"title,omitempty"`
|
||||||
Decription string `json:"decription,omitempty"`
|
Decription string `json:"decription,omitempty"`
|
||||||
Bind Bind `json:"bind,omitempty"`
|
Bind Bind `json:"bind,omitempty"`
|
||||||
|
Hooks Hooks `json:"hooks,omitempty"`
|
||||||
APIs map[string]share.API `json:"apis,omitempty"`
|
APIs map[string]share.API `json:"apis,omitempty"`
|
||||||
Columns map[string]share.Column `json:"columns,omitempty"`
|
Columns map[string]share.Column `json:"columns,omitempty"`
|
||||||
Filters map[string]share.Filter `json:"filters,omitempty"`
|
Filters map[string]share.Filter `json:"filters,omitempty"`
|
||||||
|
|
@ -28,3 +29,29 @@ type Bind struct {
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
Withs map[string]gou.With `json:"withs,omitempty"`
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// XLog 日志接口
|
// XLog 日志接口
|
||||||
|
|
@ -25,7 +27,11 @@ func Printf(format string, v ...interface{}) {
|
||||||
// Println calls Output to print to the standard logger.
|
// Println calls Output to print to the standard logger.
|
||||||
// Arguments are handled in the manner of fmt.Println.
|
// Arguments are handled in the manner of fmt.Println.
|
||||||
func Println(v ...interface{}) {
|
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).
|
// Fatalf is equivalent to Printf() followed by a call to os.Exit(1).
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue