From c3686340b29352805d378fe9b5f0f5846e98c6d2 Mon Sep 17 00:00:00 2001 From: glennliao Date: Fri, 2 Feb 2024 00:45:21 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8B=A5=E5=B9=B2=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- action/action.go | 172 +------ action/action_exec.go | 79 ++++ action/action_parse.go | 40 ++ action/executor.go | 29 +- action/hook.go | 23 +- action/node.go | 437 ++---------------- action/node_exec.go | 171 +++++++ action/node_parse.go | 120 +++++ action/util.go | 87 ++++ action/z_hook_test.go | 9 - apijson.go | 42 +- config/access.go | 3 - config/access_condition.go | 94 +++- config/access_config.go | 17 + config/action_config.go | 4 +- config/config.go | 8 +- config/functions.go | 44 +- config/query_config.go | 8 +- config/rowkeygen.go | 7 + config/tables/table.go | 87 ++-- config/z_test.go | 88 ++++ demo/READMD.md | 1 + demo/common/db/table.go | 63 +++ demo/custom_access/access.go | 50 ++ demo/custom_access/config.yaml | 3 + demo/custom_access/main.go | 51 ++ demo/go.mod | 49 ++ demo/go.sum | 227 +++++++++ demo/helloworld/config.yaml | 3 + demo/helloworld/main.go | 53 +++ drivers/goframe/all.go | 4 +- drivers/goframe/executor/action.go | 107 +++-- drivers/goframe/executor/query.go | 190 +++++--- go.mod | 49 +- go.sum | 250 +++------- model/model.go | 10 +- query/executor.go | 10 +- query/node.go | 52 +-- query/node_fetch.go | 32 ++ query/node_handler.go | 15 + query/{node_func.go => node_handler_func.go} | 6 - .../{node_query.go => node_handler_query.go} | 110 ++--- query/{node_ref.go => node_handler_ref.go} | 0 ...{node_struct.go => node_handler_struct.go} | 2 - query/query.go | 11 +- query/util.go | 11 +- test/go.mod | 47 ++ test/go.sum | 224 +++++++++ test/z_main_test.go | 24 +- 49 files changed, 2055 insertions(+), 1168 deletions(-) create mode 100644 action/action_exec.go create mode 100644 action/action_parse.go create mode 100644 action/node_exec.go create mode 100644 action/node_parse.go create mode 100644 action/util.go delete mode 100644 action/z_hook_test.go create mode 100644 config/z_test.go create mode 100644 demo/READMD.md create mode 100644 demo/common/db/table.go create mode 100644 demo/custom_access/access.go create mode 100644 demo/custom_access/config.yaml create mode 100644 demo/custom_access/main.go create mode 100644 demo/go.mod create mode 100644 demo/go.sum create mode 100644 demo/helloworld/config.yaml create mode 100644 demo/helloworld/main.go create mode 100644 query/node_fetch.go create mode 100644 query/node_handler.go rename query/{node_func.go => node_handler_func.go} (98%) mode change 100755 => 100644 rename query/{node_query.go => node_handler_query.go} (72%) mode change 100755 => 100644 rename query/{node_ref.go => node_handler_ref.go} (100%) mode change 100755 => 100644 rename query/{node_struct.go => node_handler_struct.go} (99%) mode change 100755 => 100644 create mode 100644 test/go.mod create mode 100644 test/go.sum diff --git a/action/action.go b/action/action.go index 827352f..3106cf7 100755 --- a/action/action.go +++ b/action/action.go @@ -2,25 +2,19 @@ package action import ( "context" - "strings" "github.com/glennliao/apijson-go/config" - "github.com/glennliao/apijson-go/consts" "github.com/glennliao/apijson-go/model" - "github.com/glennliao/apijson-go/query" - "github.com/glennliao/apijson-go/util" - "github.com/gogf/gf/v2/util/gconv" ) -// Action 非get查询的request表中的请求 type Action struct { - ctx context.Context - tagRequest *config.RequestConfig - method string - + ctx context.Context + err error req model.Map + ret model.Map - err error + Method string + tagRequestConfig *config.RequestConfig children map[string]*Node keyNode map[string]*Node @@ -30,173 +24,45 @@ type Action struct { // 关闭 request 验证开关, 默认否 NoRequestVerify bool - // Access *config.Access - // dbFieldStyle 数据库字段命名风格 请求传递到数据库中 DbFieldStyle config.FieldStyle - // jsonFieldStyle 数据库返回的字段 JsonFieldStyle config.FieldStyle ActionConfig *config.ActionConfig - NewQuery func(ctx context.Context, req model.Map) *query.Query - NewAction func(ctx context.Context, method string, req model.Map) *Action - HooksMap map[string][]*Hook - - ret model.Map } func New(ctx context.Context, actionConfig *config.ActionConfig, method string, req model.Map) *Action { - - request, err := checkTag(req, method, actionConfig) - if err != nil { - return &Action{ - err: err, - } - } - - delete(req, consts.Tag) - delete(req, consts.Version) - a := &Action{ ctx: ctx, - tagRequest: request, - method: method, req: req, - children: map[string]*Node{}, - keyNode: map[string]*Node{}, + Method: method, ActionConfig: actionConfig, } - return a -} - -func (a *Action) parse() error { - - if a.err != nil { - return a.err - } - - structures := a.tagRequest.Structure - - for key, v := range a.req { - - structuresKey := key - if strings.HasSuffix(key, consts.ListKeySuffix) { - structuresKey = util.RemoveSuffix(key, consts.ListKeySuffix) - } - - structure, ok := structures[key] - if !ok { - if structure, ok = structures[structuresKey]; !ok { // User[]可读取User或者User[] - return consts.NewStructureKeyNoFoundErr(key) - } - } - - var list []model.Map - _v, ok := v.(model.Map) - if ok { // 将所有node都假设成列表, 如果单个则看成一个元素的批量 - list = []model.Map{_v} - } else { - for _, m := range gconv.Maps(v) { - list = append(list, m) - } - } - - node := newNode(key, list, structure, a.tagRequest.Executor[key]) - node.ctx = a.ctx - node.Action = a - a.keyNode[key] = &node - node.keyNode = a.keyNode - err := node.parse(a.ctx, a.method) - if err != nil { - return err - } - - a.children[key] = &node - } - return nil -} - -func (a *Action) hookExecute(i int, inTransaction bool) error { - k := a.tagRequest.ExecQueue[i] - node := a.children[k] - nodeHookReq := &HookReq{ - Node: node, - Method: a.method, - ctx: a.ctx, - nextIdx: -1, - isInTransaction: inTransaction, - hooks: getHooksByAccessName(a.HooksMap, k), - } - - nodeHookReq.handler = func(ctx context.Context, n *Node, method string) error { - - if i+1 < len(a.tagRequest.ExecQueue) { - return a.hookExecute(i+1, inTransaction) - } - - // 执行完了普通hook的before,开始执行事务内 - if !inTransaction { - - transactionHandler := noTransactionHandler - - if a.tagRequest.Transaction != nil && *a.tagRequest.Transaction == true { - h := GetTransactionHandler(a.ctx, a) - if h == nil { - err := consts.NewSysErr("transaction handler is nil") - return err - } - transactionHandler = h - } - - err := transactionHandler(a.ctx, func(ctx context.Context) error { - return a.hookExecute(0, !inTransaction) - }) - - return err - } - - var err error - a.ret[k], err = node.execute(ctx, a.method) - return err - } - - err := nodeHookReq.Next() - return err -} - -func (a *Action) Result() (model.Map, error) { - - err := a.parse() + request, err := CheckTag(req, method, actionConfig) if err != nil { - return nil, err + a.err = err + return a } - a.ret = model.Map{} + a.tagRequestConfig = request - err = a.hookExecute(0, false) - if err != nil { - a.err = err - } - return a.ret, err + return a } -func checkTag(req model.Map, method string, requestCfg *config.ActionConfig) (*config.RequestConfig, error) { - _tag, ok := req[consts.Tag] - if !ok { - return nil, consts.ErrNoTag +func (a *Action) Result() (model.Map, error) { + if a.err != nil { + return nil, a.err } - tag := gconv.String(_tag) - version := req[consts.Version] - - request, err := requestCfg.GetRequest(tag, method, gconv.String(version)) - if err != nil { - return nil, err + a.parse() + if a.err != nil { + return nil, a.err } - return request, nil + a.exec() + return a.ret, a.err } diff --git a/action/action_exec.go b/action/action_exec.go new file mode 100644 index 0000000..7159468 --- /dev/null +++ b/action/action_exec.go @@ -0,0 +1,79 @@ +package action + +import ( + "context" + + "github.com/glennliao/apijson-go/model" + + "github.com/glennliao/apijson-go/consts" +) + +func (a *Action) exec() { + a.ret = model.Map{} + a.err = a.hookExecute(0, false) +} + +// hook like middleware 洋葱模型 +func (a *Action) hookExecute(i int, inTransaction bool) error { + var ( + execNodeKey = a.tagRequestConfig.ExecQueue[i] + node = a.children[execNodeKey] + ) + + nodeHookReq := &HookReq{ + ctx: a.ctx, + Node: node, + Method: a.Method, + nextId: -1, + isInTransaction: inTransaction, + hooks: getHooksByAccessName(a.HooksMap, execNodeKey), + } + + nodeHookReq.handler = func(ctx context.Context, n *Node, method string) error { + if i+1 < len(a.tagRequestConfig.ExecQueue) { + return a.hookExecute(i+1, inTransaction) + } + + // 执行完了普通hook的before,开始执行事务内 + if !inTransaction { + + transactionHandler := noTransactionHandler + + if a.tagRequestConfig.Transaction != nil && *a.tagRequestConfig.Transaction == true { + h := GetTransactionHandler(a.ctx, a) + if h == nil { + err := consts.NewSysErr("transaction handler is nil") + return err + } + transactionHandler = h + } + + err := transactionHandler(a.ctx, func(ctx context.Context) error { + return a.hookExecute(0, !inTransaction) + }) + + return err + } + + // 执行完 全部前置hook + var err error + + for _, name := range a.tagRequestConfig.ExecQueue { + node := a.children[name] + a.ret[name], err = node.execute(ctx, a.Method) + if err != nil { + return err + } + } + + return err + } + + err := nodeHookReq.Next() + return err +} + +func getHooksByAccessName(hooksMap map[string][]*Hook, accessName string) []*Hook { + hooks := append(hooksMap["*"], hooksMap[accessName]...) + return hooks +} diff --git a/action/action_parse.go b/action/action_parse.go new file mode 100644 index 0000000..5e032f9 --- /dev/null +++ b/action/action_parse.go @@ -0,0 +1,40 @@ +package action + +import ( + "github.com/glennliao/apijson-go/consts" + "github.com/gogf/gf/v2/util/gconv" + "github.com/samber/lo" +) + +func (a *Action) parse() { + structures := a.tagRequestConfig.Structure + + a.children = make(map[string]*Node) + a.keyNode = make(map[string]*Node) + + for key, v := range a.req { + + if lo.Contains([]string{consts.Tag, consts.Version}, key) { + continue + } + + structure, ok := structures[key] + if !ok { + a.err = consts.NewStructureKeyNoFoundErr(key) + return + } + + val := gconv.Map(v) + + node := newNode(a, key, val, structure) + + a.keyNode[key] = node + a.children[key] = node + + err := node.parse() + if err != nil { + a.err = consts.NewStructureKeyNoFoundErr(key) + return + } + } +} diff --git a/action/executor.go b/action/executor.go index 3ef5721..7369f95 100755 --- a/action/executor.go +++ b/action/executor.go @@ -6,27 +6,26 @@ import ( "github.com/glennliao/apijson-go/config" "github.com/glennliao/apijson-go/consts" "github.com/glennliao/apijson-go/model" - "github.com/glennliao/apijson-go/query" "github.com/samber/lo" ) -type ActionExecutorReq struct { - Method string - Table string - Data []model.Map - Where []model.Map - Access *config.AccessConfig - Config *config.ActionConfig - NewQuery func(ctx context.Context, req model.Map) *query.Query +type ExecutorReq struct { + Method string + Table string + Data model.Map + Where model.Map + AccessCondition *config.ConditionRet + Access *config.AccessConfig + Config *config.ActionConfig } -var actionExecutorMap = map[string]ActionExecutor{} +var actionExecutorMap = map[string]Executor{} -func RegExecutor(name string, e ActionExecutor) { +func RegExecutor(name string, e Executor) { actionExecutorMap[name] = e } -func GetActionExecutor(name string) (ActionExecutor, error) { +func GetActionExecutor(name string) (Executor, error) { if name == "" { name = "default" } @@ -36,12 +35,12 @@ func GetActionExecutor(name string) (ActionExecutor, error) { return nil, consts.NewSysErr("action executor not found: " + name) } -func ActionExecutorList() []string { +func ExecutorList() []string { return lo.Keys(actionExecutorMap) } -type ActionExecutor interface { - Do(ctx context.Context, req ActionExecutorReq) (ret model.Map, err error) +type Executor interface { + Do(ctx context.Context, req ExecutorReq) (ret model.Map, err error) } // TransactionHandler 事务处理函数 diff --git a/action/hook.go b/action/hook.go index 6e25377..564b506 100755 --- a/action/hook.go +++ b/action/hook.go @@ -10,7 +10,7 @@ type HookReq struct { Method string ctx context.Context hooks []*Hook - nextIdx int + nextId int isInTransaction bool handler finishHandler } @@ -28,16 +28,15 @@ func (r *HookReq) IsDelete() bool { } func (r *HookReq) Next() error { - for { var h *Hook - for r.nextIdx+1 < len(r.hooks) && h == nil { + for r.nextId+1 < len(r.hooks) && h == nil { - r.nextIdx++ + r.nextId++ - _h := r.hooks[r.nextIdx] + _h := r.hooks[r.nextId] if r.isInTransaction { if _h.HandlerInTransaction == nil { @@ -54,7 +53,7 @@ func (r *HookReq) Next() error { } if h != nil { - if r.nextIdx < len(r.hooks) { + if r.nextId < len(r.hooks) { if r.isInTransaction { return h.HandlerInTransaction(r.ctx, r) } @@ -65,7 +64,14 @@ func (r *HookReq) Next() error { return r.handler(r.ctx, r.Node, r.Method) } +} +func (r *HookReq) RowKey() any { + id := r.Node.Data[r.Node.RowKey] + if r.IsPut() { + id = r.Node.Where[r.Node.RowKey] + } + return id } type Hook struct { @@ -77,8 +83,3 @@ type Hook struct { } type finishHandler func(ctx context.Context, n *Node, method string) error - -func getHooksByAccessName(hooksMap map[string][]*Hook, accessName string) []*Hook { - hooks := append(hooksMap["*"], hooksMap[accessName]...) - return hooks -} diff --git a/action/node.go b/action/node.go index 761e3d5..4ccb408 100755 --- a/action/node.go +++ b/action/node.go @@ -2,428 +2,55 @@ package action import ( "context" - "net/http" "strings" - "github.com/glennliao/apijson-go/config" "github.com/glennliao/apijson-go/consts" - "github.com/glennliao/apijson-go/model" "github.com/glennliao/apijson-go/util" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/samber/lo" + + "github.com/glennliao/apijson-go/config" + "github.com/glennliao/apijson-go/model" ) type Node struct { - req []model.Map - ctx context.Context - Action *Action - Key string - IsList bool - tableName string - Role string - - Data []model.Map // 需写入数据库的数据 - Where []model.Map // 条件 - Ret model.Map // 节点返回值 - RowKey string // 主键 - + ctx context.Context + // key为原始请求的key + Key string + AccessName string + req model.Map + Ret model.Map + + Action *Action + Role string + + Data model.Map + Where model.Map + AccessCondition *config.ConditionRet + + TableName string + RowKey string structure *config.Structure executor string keyNode map[string]*Node - - // access *config.Access } -func newNode(key string, req []model.Map, structure *config.Structure, executor string) Node { - - n := Node{ - Key: key, req: req, structure: structure, executor: executor, - } - - n.Data = []model.Map{} - n.Where = []model.Map{} - - for _ = range n.req { - - n.Data = append(n.Data, model.Map{}) - n.Where = append(n.Where, model.Map{}) - +func newNode(a *Action, key string, req model.Map, structure *config.Structure) *Node { + n := &Node{ + ctx: a.ctx, + Action: a, + keyNode: a.keyNode, + Key: key, + AccessName: key, + req: req, + structure: structure, + executor: a.tagRequestConfig.Executor[key], + Data: make(model.Map), + Where: make(model.Map), } if strings.HasSuffix(key, consts.ListKeySuffix) { - n.Key = util.RemoveSuffix(key, consts.ListKeySuffix) - n.IsList = true + n.AccessName = util.RemoveSuffix(key, consts.ListKeySuffix) } return n } - -// parse req data to data/where -func (n *Node) parseReq(method string) { - - for i, item := range n.req { - for key, val := range item { - - if key == consts.Role { - n.Role = util.String(val) - continue - } - - key = n.Action.DbFieldStyle(n.ctx, n.tableName, key) - - switch method { - case http.MethodPost: - n.Data[i][key] = val - case http.MethodDelete: - n.Where[i][key] = val - case http.MethodPut: - if key == n.RowKey || key == n.RowKey+consts.OpIn { - n.Where[i][key] = val - } else { - n.Data[i][key] = val - } - } - - } - } -} - -// parse node -func (n *Node) parse(ctx context.Context, method string) error { - - key := n.Key - if strings.HasSuffix(key, consts.ListKeySuffix) { - key = util.RemoveSuffix(key, consts.ListKeySuffix) - } - access, err := n.Action.ActionConfig.GetAccessConfig(key, true) - - if err != nil { - return err - } - - n.tableName = access.Name - n.RowKey = access.RowKey - - // 0. 角色替换 - - err = n.roleUpdate() - if err != nil { - return err - } - var accessRoles []string - if n.Action.NoAccessVerify == false { - // 1. 检查权限, 无权限就不用做参数检查了 - - switch method { - case http.MethodPost: - accessRoles = access.Post - case http.MethodPut: - accessRoles = access.Put - case http.MethodDelete: - accessRoles = access.Delete - } - - err = n.checkAccess(ctx, method, accessRoles) - if err != nil { - return err - } - } - - // 2. 检查参数 - err = n.checkReq() - if err != nil { - return err - } - - // 3. get where by accessCondition - err = n.whereUpdate(ctx, method, accessRoles) - - n.parseReq(method) - - return err -} - -// update node role -func (n *Node) roleUpdate() error { - - if val, exists := n.structure.Insert[consts.Role]; exists { - if n.Role == "" { - n.Role = util.String(val) - } - } - - if val, exists := n.structure.Update[consts.Role]; exists { - n.Role = util.String(val) - } - - return nil -} - -func (n *Node) checkAccess(ctx context.Context, method string, accessRoles []string) error { - - role, err := n.Action.ActionConfig.DefaultRoleFunc()(ctx, config.RoleReq{ - AccessName: n.tableName, - Method: method, - NodeRole: n.Role, - }) - - if err != nil { - return err - } - - if role == consts.DENY { - return consts.NewDenyErr(n.Key, n.Role) - } - - n.Role = role - - if !lo.Contains(accessRoles, role) { - return consts.NewNoAccessErr(n.Key, n.Role) - } - - return nil -} - -func (n *Node) whereUpdate(ctx context.Context, method string, accessRoles []string) error { - - for i, item := range n.req { - - condition := config.NewConditionRet() - - req := model.Map{} - - for k, v := range item { - k := n.Action.DbFieldStyle(ctx, n.RowKey, k) - req[k] = v - } - - conditionReq := config.ConditionReq{ - AccessName: n.Key, - TableAccessRoleList: accessRoles, - Method: method, - NodeRole: n.Role, - NodeReq: req, - } - - err := n.Action.ActionConfig.ConditionFunc(ctx, conditionReq, condition) - - if err != nil { - return err - } - - if method == http.MethodPost { - for k, v := range condition.AllWhere() { - n.Data[i][k] = v - } - } else { - for k, v := range condition.AllWhere() { - n.Where[i][k] = v - } - } - } - - return nil -} - -func (n *Node) checkReq() error { - - for _, item := range n.req { - // must - for _, key := range n.structure.Must { - if _, exists := item[key]; !exists { - return consts.NewStructureKeyNoFoundErr(n.Key + "." + key) - } - } - - // refuse - if len(n.structure.Refuse) > 0 && n.structure.Refuse[0] == "!" { - if len(n.structure.Must) == 0 { - return consts.NewValidStructureErr("REFUSE为!时必须指定MUST:" + n.Key) - } - - for key, _ := range item { - if !lo.Contains(n.structure.Must, key) { - return consts.NewValidStructureErr("不能包含:" + n.Key + "." + key) - } - } - - } else { - for _, key := range n.structure.Refuse { - if _, exists := item[key]; exists { - return consts.NewValidStructureErr("不能包含:" + n.Key + "." + key) - } - } - } - } - - return nil -} - -// reqUpdate 处理 Update/Insert等 -func (n *Node) reqUpdate() error { - - for i, _ := range n.req { - for key, updateVal := range n.structure.Update { - - if strings.HasSuffix(key, consts.FunctionsKeySuffix) { - - k := key[0 : len(key)-2] - - // call functions - { - actionConfig := n.Action.ActionConfig - - functionName, paramKeys := util.ParseFunctionsStr(updateVal.(string)) - - _func := actionConfig.Func(functionName) - - param := model.Map{} - for paramI, item := range _func.ParamList { - if item.Name == consts.FunctionOriReqParam { - param[item.Name] = n.Data[i] - } else { - param[item.Name] = n.Data[i][paramKeys[paramI]] - } - } - - val, err := actionConfig.CallFunc(n.ctx, functionName, param) - if err != nil { - return err - } - if val != nil { - n.Data[i][k] = val - } - } - - } else { - n.Data[i][key] = updateVal - } - } - - for key, updateVal := range n.structure.Insert { - if _, exists := n.Data[i][key]; !exists { - n.Data[i][key] = updateVal - } - } - - } - - return nil -} - -// reqUpdate 处理 Update/Insert等 (事务内) -func (n *Node) reqUpdateBeforeDo() error { - - for i, _ := range n.req { - - for k, v := range n.Data[i] { - // 处理 ref - if strings.HasSuffix(k, consts.RefKeySuffix) { - refNodeKey, refCol := util.ParseRefCol(v.(string)) - if strings.HasSuffix(refNodeKey, consts.ListKeySuffix) { // 双列表 - n.Data[i][k] = n.keyNode[refNodeKey].Data[i][n.Action.DbFieldStyle(n.ctx, n.tableName, refCol)] - } else { - n.Data[i][k] = n.keyNode[refNodeKey].Data[0][n.Action.DbFieldStyle(n.ctx, n.tableName, refCol)] - } - } - } - } - - return nil -} - -func (n *Node) do(ctx context.Context, method string) (ret model.Map, err error) { - - var rowKeyVal model.Map - var rowKey string - - access, err := n.Action.ActionConfig.GetAccessConfig(n.Key, true) - if err != nil { - return nil, err - } - - switch method { - case http.MethodPost: - - if access.RowKeyGen != "" { - for i, _ := range n.Data { - - rowKeyVal, err = n.Action.ActionConfig.RowKeyGen(ctx, access.RowKeyGen, n.Key, n.tableName, n.Data[i]) - if err != nil { - return nil, gerror.Wrap(err, "RowKeyGen") - } - - for k, v := range rowKeyVal { - if k == consts.RowKey { - n.Data[i][access.RowKey] = v - } else { - n.Data[i][k] = v - } - } - - } - } - - rowKey = access.RowKey - - case http.MethodPut: - case http.MethodDelete: - - default: - return nil, consts.NewMethodNotSupportErr(method) - } - - executor, err := GetActionExecutor(n.executor) - if err != nil { - return nil, err - } - - ret, err = executor.Do(ctx, ActionExecutorReq{ - Method: method, - Table: n.tableName, - Data: n.Data, - Where: n.Where, - Access: access, - Config: n.Action.ActionConfig, - NewQuery: n.Action.NewQuery, - }) - - if err != nil { - return nil, err - } - - if len(n.Data) == 1 { - - jsonStyle := n.Action.JsonFieldStyle - if rowKeyVal != nil { - for k, v := range rowKeyVal { - if k == consts.RowKey { - ret[jsonStyle(ctx, n.tableName, rowKey)] = v - } else { - ret[jsonStyle(ctx, n.tableName, k)] = v - } - } - } - } - - n.Ret = ret - - return -} - -func (n *Node) execute(ctx context.Context, method string) (model.Map, error) { - - err := n.reqUpdateBeforeDo() - if err != nil { - return nil, err - } - - ret, err := n.do(ctx, method) - if err != nil { - return nil, err - } - - n.Ret = ret - return n.Ret, nil -} diff --git a/action/node_exec.go b/action/node_exec.go new file mode 100644 index 0000000..7d35727 --- /dev/null +++ b/action/node_exec.go @@ -0,0 +1,171 @@ +package action + +import ( + "context" + "net/http" + "strings" + + "github.com/glennliao/apijson-go/config" + + "github.com/glennliao/apijson-go/util" + + "github.com/glennliao/apijson-go/consts" + "github.com/glennliao/apijson-go/model" + "github.com/gogf/gf/v2/errors/gerror" +) + +func (n *Node) execute(ctx context.Context, method string) (model.Map, error) { + err := n.updateReqBeforeDo() + if err != nil { + return nil, err + } + + ret, err := n.do(ctx, method) + if err != nil { + return nil, err + } + + n.Ret = ret + + return n.Ret, nil +} + +// updateReqBeforeDo +// like ref -> 'xxx@' +func (n *Node) updateReqBeforeDo() error { + for key, v := range n.Data { + // ref + if strings.HasSuffix(key, consts.RefKeySuffix) { + refNodeKey, refCol := util.ParseRefCol(v.(string)) + refNodeKeyCol := n.Action.DbFieldStyle(n.ctx, n.TableName, refCol) + + n.Data[key] = n.keyNode[refNodeKey].Data[refNodeKeyCol] + + //if strings.HasSuffix(refNodeKey, consts.ListKeySuffix) { // 双列表 + // n.Data[key] = n.keyNode[refNodeKey].Data[n.Action.DbFieldStyle(n.ctx, n.TableName, refCol)] + //} + } + } + return nil +} + +func (n *Node) do(ctx context.Context, method string) (ret model.Map, err error) { + access, err := n.Action.ActionConfig.GetAccessConfig(n.Key, n.Action.NoAccessVerify) + if err != nil { + return nil, err + } + + var ( + rowKeyVal model.Map + rowKey = access.RowKey + ) + + // gen rowKey + if method == http.MethodPost && access.RowKeyGen != "" { + + rowKeyVal, err = n.genRowKey(access) + if err != nil { + return nil, err + } + } + + executor, err := GetActionExecutor(n.executor) + if err != nil { + return nil, err + } + + ret, err = executor.Do(ctx, ExecutorReq{ + Method: method, + Table: n.TableName, + Data: n.Data, + Where: n.Where, + Access: access, + AccessCondition: n.AccessCondition, + Config: n.Action.ActionConfig, + }) + if err != nil { + return nil, err + } + + // return rowKey + if rowKeyVal != nil { + jsonStyle := n.Action.JsonFieldStyle + for k, v := range rowKeyVal { + if k == consts.RowKey { + ret[jsonStyle(ctx, n.TableName, rowKey)] = v + } else { + ret[jsonStyle(ctx, n.TableName, k)] = v + } + } + } + + n.Ret = ret + + return +} + +func (n *Node) genRowKey(access *config.AccessConfig) (rowKeyVal model.Map, err error) { + rowKeyVal, err = n.Action.ActionConfig.RowKeyGen(n.ctx, access.RowKeyGen, n.Key, n.TableName, n.Data) + if err != nil { + return nil, gerror.Wrap(err, "RowKeyGen") + } + + for k, v := range rowKeyVal { + if k == consts.RowKey { + n.Data[access.RowKey] = v + } else { + n.Data[k] = v + } + } + return +} + +// reqUpdate 处理 structure 的 Update/Insert等 +//func (n *Node) reqUpdate() error { +// for i := range n.req { +// for key, updateVal := range n.structure.Update { +// if strings.HasSuffix(key, consts.FunctionsKeySuffix) { +// +// k := key[0 : len(key)-2] +// +// // call functions +// { +// actionConfig := n.Action.ActionConfig +// +// functionName, paramKeys := util.ParseFunctionsStr(updateVal.(string)) +// +// _func := actionConfig.Func(functionName) +// +// param := model.Map{} +// for paramI, item := range _func.ParamList { +// if item.Name == consts.FunctionOriReqParam { +// param[item.Name] = n.Data[i] +// } else { +// param[item.Name] = n.Data[i][paramKeys[paramI]] +// } +// } +// +// val, err := actionConfig.CallFunc(n.ctx, functionName, param) +// if err != nil { +// return err +// } +// if val != nil { +// n.Data[i][k] = val +// } +// } +// +// } else { +// n.Data[i][key] = updateVal +// } +// } +// +// for key, updateVal := range n.structure.Insert { +// if _, exists := n.Data[i][key]; !exists { +// n.Data[i][key] = updateVal +// } +// } +// +// } +// +// return nil +//} diff --git a/action/node_parse.go b/action/node_parse.go new file mode 100644 index 0000000..3d9fac8 --- /dev/null +++ b/action/node_parse.go @@ -0,0 +1,120 @@ +package action + +import ( + "context" + "net/http" + + "github.com/glennliao/apijson-go/config" + "github.com/glennliao/apijson-go/consts" + "github.com/glennliao/apijson-go/model" + "github.com/glennliao/apijson-go/util" +) + +// parse node +func (n *Node) parse() error { + ctx, method := n.ctx, n.Action.Method + + access, err := n.Action.ActionConfig.GetAccessConfig(n.Key, n.Action.NoAccessVerify) + if err != nil { + return err + } + + n.TableName = access.Name + n.RowKey = access.RowKey + + accessRoles := access.GetAccessRoles(method) + + // 0. 角色替换 + + //err = n.roleUpdate() + //if err != nil { + // return err + //} + + if !n.Action.NoAccessVerify { + err = CheckRoleAccess(ctx, n, accessRoles) + if err != nil { + return err + } + } + + err = CheckReqStructure(n.req, n.structure) + if err != nil { + return err + } + + req := model.Map{} + for key, v := range n.req { + k := n.Action.DbFieldStyle(ctx, n.TableName, key) + req[k] = v + } + n.req = req + + err = n.parseAccessCondition(ctx, accessRoles) + if err != nil { + return err + } + + n.parseReq() + + return err +} + +// parseAccessCondition +func (n *Node) parseAccessCondition(ctx context.Context, accessRoles []string) error { + n.AccessCondition = &config.ConditionRet{} + + conditionReq := config.ConditionReq{ + AccessName: n.Key, + TableAccessRoleList: accessRoles, + Method: n.Action.Method, + NodeRole: n.Role, + NodeReq: n.req, + } + + err := n.Action.ActionConfig.ConditionFunc(ctx, conditionReq, n.AccessCondition) + + return err +} + +// parse req to data/where +func (n *Node) parseReq() { + for key, val := range n.req { + + if key == consts.Role { + n.Role = util.String(val) + continue + } + + // key = n.Action.DbFieldStyle(n.ctx, n.TableName, key) + + switch n.Action.Method { + case http.MethodPost: + n.Data[key] = val + case http.MethodDelete: + n.Where[key] = val + case http.MethodPut: + if key == n.RowKey || key == n.RowKey+consts.OpIn { + // only rowKey is where, others is update data + n.Where[key] = val + } else { + n.Data[key] = val + } + } + + } +} + +// update node role +//func (n *Node) roleUpdate() error { +// if val, exists := n.structure.Insert[consts.Role]; exists { +// if n.Role == "" { +// n.Role = util.String(val) +// } +// } +// +// if val, exists := n.structure.Update[consts.Role]; exists { +// n.Role = util.String(val) +// } +// return nil +//} diff --git a/action/util.go b/action/util.go new file mode 100644 index 0000000..afdb0b6 --- /dev/null +++ b/action/util.go @@ -0,0 +1,87 @@ +package action + +import ( + "context" + + "github.com/samber/lo" + + "github.com/glennliao/apijson-go/config" + "github.com/glennliao/apijson-go/consts" + "github.com/glennliao/apijson-go/model" + "github.com/gogf/gf/v2/util/gconv" +) + +// CheckTag +// check tag is valid +func CheckTag(req model.Map, method string, requestCfg *config.ActionConfig) (*config.RequestConfig, error) { + _tag, ok := req[consts.Tag] + if !ok { + return nil, consts.ErrNoTag + } + + tag := gconv.String(_tag) + version := req[consts.Version] + + request, err := requestCfg.GetRequest(tag, method, gconv.String(version)) + if err != nil { + return nil, err + } + + return request, nil +} + +// CheckRoleAccess +// check role access +func CheckRoleAccess(ctx context.Context, node *Node, accessRoles []string) error { + roleFunc := node.Action.ActionConfig.DefaultRoleFunc() + + role, err := roleFunc(ctx, config.RoleReq{ + AccessName: node.TableName, + Method: node.Action.Method, + NodeRole: node.Role, + }) + if err != nil { + return err + } + + if role == consts.DENY { + return consts.NewDenyErr(node.Key, node.Role) + } + + node.Role = role + + if !lo.Contains(accessRoles, role) { + return consts.NewNoAccessErr(node.Key, node.Role) + } + + return nil +} + +func CheckReqStructure(req model.Map, structure *config.Structure) error { + // must + for _, key := range structure.Must { + if _, exists := req[key]; !exists { + return consts.NewStructureKeyNoFoundErr(key) + } + } + + // refuse + if len(structure.Refuse) > 0 && structure.Refuse[0] == "!" { + if len(structure.Must) == 0 { + return consts.NewValidStructureErr("must set 'MUST' when 'REFUSE' is !") + } + for key := range req { + if !lo.Contains(structure.Must, key) { + return consts.NewValidStructureErr("can't has " + key) + } + } + } else { + for _, key := range structure.Refuse { + if _, exists := req[key]; exists { + return consts.NewValidStructureErr("refuse " + key) + } + } + } + + return nil +} diff --git a/action/z_hook_test.go b/action/z_hook_test.go deleted file mode 100644 index adaf71a..0000000 --- a/action/z_hook_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package action - -import ( - "testing" -) - -func TestHook(t *testing.T) { - -} diff --git a/apijson.go b/apijson.go index 1aa6216..9d15876 100755 --- a/apijson.go +++ b/apijson.go @@ -9,37 +9,28 @@ import ( "github.com/glennliao/apijson-go/query" ) -type Plugin interface { - Install(ctx context.Context, a *ApiJson) -} +const CtxKey = "apiJsonApp" type ApiJson struct { - config *config.Config - Debug bool // 是否开启debug模式, 显示每步骤 ctx context.Context + config *config.Config + Debug bool actionHooks []action.Hook actionHookMap map[string][]*action.Hook } -var DefaultApiJson = New() - func New() *ApiJson { - a := &ApiJson{} - a.config = config.New() - a.ctx = context.Background() + a := &ApiJson{ + ctx: context.Background(), + config: config.New(), + } return a } -// Load load for defaultApiJson, 简化使用 -func Load(apps ...func(ctx context.Context, a *ApiJson)) *ApiJson { - - for _, app := range apps { - DefaultApiJson.Use(app) - } - - DefaultApiJson.Load() - return DefaultApiJson +func (a *ApiJson) SetCtx(ctx context.Context) *ApiJson { + a.ctx = ctx + return a } func (a *ApiJson) Use(p ...func(ctx context.Context, a *ApiJson)) *ApiJson { @@ -49,8 +40,8 @@ func (a *ApiJson) Use(p ...func(ctx context.Context, a *ApiJson)) *ApiJson { return a } -func (a *ApiJson) Load() { - a.config.ReLoad() +func (a *ApiJson) Load() error { + return a.config.ReLoad(a.ctx) } func (a *ApiJson) Config() *config.Config { @@ -58,6 +49,8 @@ func (a *ApiJson) Config() *config.Config { } func (a *ApiJson) NewQuery(ctx context.Context, req model.Map) *query.Query { + ctx = context.WithValue(ctx, CtxKey, a) + q := query.New(ctx, a.Config().QueryConfig(), req) q.DbMeta = a.config.DbMeta @@ -71,14 +64,13 @@ func (a *ApiJson) NewQuery(ctx context.Context, req model.Map) *query.Query { } func (a *ApiJson) NewAction(ctx context.Context, method string, req model.Map) *action.Action { + ctx = context.WithValue(ctx, CtxKey, a) act := action.New(ctx, a.Config().ActionConfig(), method, req) act.NoAccessVerify = a.config.Access.NoVerify act.DbFieldStyle = a.config.DbFieldStyle act.JsonFieldStyle = a.config.JsonFieldStyle - act.NewQuery = a.NewQuery - act.HooksMap = a.actionHookMap return act @@ -92,3 +84,7 @@ func (a *ApiJson) RegActionHook(hook action.Hook) { a.actionHookMap[item] = append(a.actionHookMap[item], &hook) } } + +func GetApiJson(ctx context.Context) *ApiJson { + return ctx.Value(CtxKey).(*ApiJson) +} diff --git a/config/access.go b/config/access.go index 42906b1..af0a0fd 100755 --- a/config/access.go +++ b/config/access.go @@ -50,9 +50,6 @@ type Access struct { } func NewAccess() *Access { - - // fixme 统一access字段名大小写问题 - // fixme a := &Access{} a.ConditionFunc = defaultCondition a.DefaultRoleFunc = defaultRole diff --git a/config/access_condition.go b/config/access_condition.go index d2808d3..681d931 100644 --- a/config/access_condition.go +++ b/config/access_condition.go @@ -1,33 +1,95 @@ package config -import ( - "github.com/glennliao/apijson-go/consts" +const ( + OpEq = iota + OpNotEq + OpIn + OpRaw ) +type WhereItem struct { + Args interface{} + Column string + Op int +} + type ConditionRet struct { - condition map[string]any - rawCondition map[string][]any + conditionList []WhereItem + builder []*ConditionRet + isEmptyResult bool } func NewConditionRet() *ConditionRet { - c := ConditionRet{ - condition: map[string]any{}, - rawCondition: map[string][]any{}, - } + c := ConditionRet{} return &c } -func (c *ConditionRet) Add(k string, v any) { - c.condition[k] = v +func (c *ConditionRet) Eq(k string, v any) { + c.where(OpEq, k, v) } -func (c *ConditionRet) AddRaw(k string, v ...any) { - c.rawCondition[k] = v +func (c *ConditionRet) NotEq(k string, v any) { + c.where(OpNotEq, k, v) } -func (c *ConditionRet) AllWhere() map[string]any { - if len(c.rawCondition) > 0 { - c.condition[consts.Raw] = c.rawCondition +func (c *ConditionRet) In(k string, v any) { + c.where(OpIn, k, v) +} + +func (c *ConditionRet) where(op int, k string, v any) { + item := WhereItem{ + Op: op, + Args: v, + Column: k, } - return c.condition + c.conditionList = append(c.conditionList, item) +} + +//func (c *ConditionRet) orWhere(op int, k string, v any) { +// // TODO OR +// item := WhereItem{ +// Op: op, +// Args: v, +// Column: k, +// } +// c.conditionList = append(c.conditionList, item) +//} + +func (c *ConditionRet) Raw(k string, v ...any) { + item := WhereItem{ + Column: k, + Args: v, + } + c.conditionList = append(c.conditionList, item) +} + +func (c *ConditionRet) OrRaw(k string, v ...any) { + prefix := " OR " + if len(c.conditionList) == 0 { + prefix = "" + } + item := WhereItem{ + Column: prefix + k, + Args: v, + } + c.conditionList = append(c.conditionList, item) +} + +// SetEmptyResult 设置结果为空,不进行实际查询(例如sql) +func (c *ConditionRet) SetEmptyResult() { + c.isEmptyResult = true +} + +func (c *ConditionRet) IsEmptyResult() bool { + return c.isEmptyResult +} + +func (c *ConditionRet) AllCondition() ([]WhereItem, []*ConditionRet) { + return c.conditionList, c.builder +} + +func (c *ConditionRet) NewBuilder() *ConditionRet { + b := &ConditionRet{} + c.builder = append(c.builder, b) + return b } diff --git a/config/access_config.go b/config/access_config.go index 7380735..83f8284 100755 --- a/config/access_config.go +++ b/config/access_config.go @@ -57,6 +57,23 @@ func (a *AccessConfig) GetFieldsGetInByRole(role string) map[string][]string { return inFieldsMap } +func (a *AccessConfig) GetAccessRoles(method string) []string { + switch method { + case http.MethodGet: + return a.Get + case http.MethodHead: + return a.Head + case http.MethodPost: + return a.Post + case http.MethodPut: + return a.Put + case http.MethodDelete: + return a.Delete + } + + return make([]string, 0) +} + func (a *Access) GetAccess(accessName string, noVerify bool) (*AccessConfig, error) { access, ok := a.accessConfigMap[accessName] diff --git a/config/action_config.go b/config/action_config.go index e04db1d..4f7683f 100755 --- a/config/action_config.go +++ b/config/action_config.go @@ -33,6 +33,7 @@ func (c *ActionConfig) GetAccessConfig(key string, noVerify bool) (*AccessConfig func (c *ActionConfig) Func(name string) *Func { return c.functions.funcMap[name] } + func (c *ActionConfig) CallFunc(ctx context.Context, name string, param model.Map) (res any, err error) { return c.functions.Call(ctx, name, param) } @@ -46,7 +47,6 @@ func (c *ActionConfig) ConditionFunc(ctx context.Context, req ConditionReq, cond } func (c *ActionConfig) RowKeyGen(ctx context.Context, genFuncName string, accessName string, tableName string, data model.Map) (model.Map, error) { - var paramKeys []string if strings.Contains(genFuncName, "(") { @@ -62,7 +62,7 @@ func (c *ActionConfig) RowKeyGen(ctx context.Context, genFuncName string, access } if len(paramKeys) > 0 { - var param = model.FuncParam{} + param := model.FuncParam{} for i, item := range f.ParamList { if len(paramKeys) >= i { param[item.Name] = gvar.New(paramKeys[i]) diff --git a/config/config.go b/config/config.go index f8722eb..a7cb4f1 100755 --- a/config/config.go +++ b/config/config.go @@ -78,12 +78,9 @@ func New() *Config { return c } -func (c *Config) ReLoad() { - +func (c *Config) ReLoad(ctx context.Context) (err error) { accessConfigMap := make(map[string]AccessConfig) - ctx := context.Background() - accessListProvider := accessListProviderMap[c.AccessListProvider] if accessListProvider != nil { @@ -105,7 +102,7 @@ func (c *Config) ReLoad() { access.FieldsGet["default"] = &FieldsGetValue{} } - for role, _ := range access.FieldsGet { + for role := range access.FieldsGet { if access.FieldsGet[role].MaxCount == nil { access.FieldsGet[role].MaxCount = &defaultMaxCount } @@ -143,6 +140,7 @@ func (c *Config) ReLoad() { defaultRoleFunc: c.Access.DefaultRoleFunc, } + return } func (c *Config) QueryConfig() *QueryConfig { diff --git a/config/functions.go b/config/functions.go index dfa0649..05f9fc6 100755 --- a/config/functions.go +++ b/config/functions.go @@ -3,10 +3,10 @@ package config import ( "context" "fmt" + "reflect" "github.com/glennliao/apijson-go/model" "github.com/gogf/gf/v2/container/gvar" - "github.com/gogf/gf/v2/frame/g" ) const ( @@ -28,9 +28,7 @@ type Func struct { Desc string // 描述 // 参数可直接读取函数参数传递过来的, ''括起来 ParamList []ParamItem // 参数列表 // fixme 限制参数来源,强制用户传递的无法覆盖内部的,减免权限的重复判断, 参数校验限制 , v (最大值,最小值,默认值, 自定义校验。 使用gvaild) - - Batch bool // 是否为批量处理, 例如在获取列表后一次性将id传入, 然后按照传入的参数数组返回结果数组 - Handler func(ctx context.Context, param model.FuncParam) (res any, err error) + Handler func(ctx context.Context, param model.FuncParam) (res any, err error) } type functions struct { @@ -44,8 +42,44 @@ func (f *functions) Bind(name string, _func Func) { f.funcMap[name] = &_func } -func (f *functions) Call(ctx context.Context, name string, param g.Map) (any, error) { +func (f *functions) BindFunc(name string, function interface{}) { + funcValue := reflect.ValueOf(function) + // todo check func param and return + + reqType := funcValue.Type().In(1) + + f.Bind(name, Func{ + Handler: func(ctx context.Context, param model.FuncParam) (res any, err error) { + //if funcInfo.Type().In(1).Kind() == reflect.Ptr { + //} else { + // inputObject = reflect.New(funcInfo.Type().In(1).Elem()).Elem() + // err = param.Scan(inputObject.Addr().Interface()) + //} + + inputObject := reflect.New(reqType.Elem()) + err = param.Scan(inputObject.Interface()) + if err != nil { + return nil, err + } + + inputValues := []reflect.Value{ + reflect.ValueOf(ctx), + inputObject, + } + + results := funcValue.Call(inputValues) + + if _err, ok := results[0].Interface().(error); ok { + err = _err + } + + return results[0], err + }, + Desc: "", + }) +} +func (f *functions) Call(ctx context.Context, name string, param model.Map) (any, error) { params := map[string]model.Var{} for k, v := range param { params[k] = gvar.New(v) diff --git a/config/query_config.go b/config/query_config.go index e5798c0..2cb7294 100755 --- a/config/query_config.go +++ b/config/query_config.go @@ -39,6 +39,7 @@ func (c *QueryConfig) CallFunc(ctx context.Context, name string, param model.Map func (c *QueryConfig) MaxTreeDeep() int { return c.maxTreeDeep } + func (c *QueryConfig) MaxTreeWidth() int { return c.maxTreeWidth } @@ -74,7 +75,6 @@ func (c *ExecutorConfig) TableColumns() []string { } func (c *ExecutorConfig) GetFieldsGetByRole() *FieldsGetValue { - if val, exists := c.accessConfig.FieldsGet[c.role]; exists { return val } @@ -83,6 +83,10 @@ func (c *ExecutorConfig) GetFieldsGetByRole() *FieldsGetValue { } func (c *ExecutorConfig) GetFieldsGetOutByRole() []string { + if c.accessConfig.FieldsGet == nil { + return make([]string, 0) + } + var fieldsMap map[string]string if val, exists := c.accessConfig.FieldsGet[c.role]; exists { @@ -119,10 +123,8 @@ func (c *ExecutorConfig) AccessRoles() []string { return c.accessConfig.Delete } return []string{} - } func (c *ExecutorConfig) Executor() string { return c.accessConfig.Executor - } diff --git a/config/rowkeygen.go b/config/rowkeygen.go index 4bfdd74..a928e26 100755 --- a/config/rowkeygen.go +++ b/config/rowkeygen.go @@ -36,6 +36,7 @@ func NewRowKeyGenRet() *RowKeyGenRet { func (r *RowKeyGenRet) RowKey(id any) { r.data[consts.RowKey] = id } + func (r *RowKeyGenRet) RowKeys(d model.Map) { for k, v := range d { r.data[k] = v @@ -57,6 +58,12 @@ func (r *RowKeyGenRet) RowKeys(d model.Map) { // } func (c *Config) RowKeyGenFunc(f RowKeyGenerator) { + if f.Handler == nil { + panic("RowKeyGenFunc Handler is nil") + } + if _, ok := c.rowKeyGenFuncMap[f.Name]; ok { + panic("RowKeyGenFunc " + f.Name + " already exists") + } c.rowKeyGenFuncMap[f.Name] = f } diff --git a/config/tables/table.go b/config/tables/table.go index bcb368c..92f88ba 100755 --- a/config/tables/table.go +++ b/config/tables/table.go @@ -1,58 +1,57 @@ package tables import ( - "github.com/glennliao/table-sync/tablesync" "time" ) type Access struct { - tablesync.TableMeta `tableName:"_access" charset:"utf8mb4" comment:"权限配置"` - Id uint32 `ddl:"primaryKey"` - Debug int8 `ddl:"not null;default:0;comment:是否调试表,开发环境可用"` - Name string `ddl:"size:32;not null;comment:实际表名"` - Alias string `ddl:"size:32;uniqueIndex;comment:表别名,外部调用"` - Get string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许get的权限角色列表"` - Head string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许head的权限角色列表"` - Gets string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许gets的权限角色列表"` - Heads string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许heads的权限角色列表"` - Post string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许post的权限角色列表"` - Put string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许put的权限角色列表"` - Delete string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许delete的权限角色列表"` - CreatedAt *time.Time `ddl:"notnull;default:CURRENT_TIMESTAMP;comment:创建时间"` - Detail string `ddl:"size:512;"` - RowKey string `ddl:"size:32;comment:(逻辑)主键字段名,联合主键使用,分割"` - FieldsGet map[string]any `ddl:"type:json;comment:get查询时字段配置"` - RowKeyGen string `ddl:"comment:rowKey生成策略"` - Executor string `ddl:"size:32;comment:执行器"` + // tablesync.TableMeta `tableName:"_access" charset:"utf8mb4" comment:"权限配置"` + Id uint32 `ddl:"primaryKey"` + Debug int8 `ddl:"not null;default:0;comment:是否调试表,开发环境可用"` + Name string `ddl:"size:32;not null;comment:实际表名"` + Alias string `ddl:"size:32;uniqueIndex;comment:表别名,外部调用"` + Get string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许get的权限角色列表"` + Head string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许head的权限角色列表"` + Gets string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许gets的权限角色列表"` + Heads string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许heads的权限角色列表"` + Post string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许post的权限角色列表"` + Put string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许put的权限角色列表"` + Delete string `ddl:"size:128;not null;default:'LOGIN,OWNER,ADMIN';comment:允许delete的权限角色列表"` + CreatedAt *time.Time `ddl:"notnull;default:CURRENT_TIMESTAMP;comment:创建时间"` + Detail string `ddl:"size:512;"` + RowKey string `ddl:"size:32;comment:(逻辑)主键字段名,联合主键使用,分割"` + FieldsGet map[string]any `ddl:"type:json;comment:get查询时字段配置"` + RowKeyGen string `ddl:"comment:rowKey生成策略"` + Executor string `ddl:"size:32;comment:执行器"` } type Request struct { - tablesync.TableMeta `tableName:"_request" charset:"utf8mb4" comment:"请求参数校验配置"` - Id uint32 `ddl:"primaryKey"` - Debug int8 `ddl:"not null;default:0;comment:是否调试,开发环境可用"` - Tag string `ddl:"not null;size:32;not null;comment:标签名(表别名)"` - Version string `ddl:"not null;size:8;comment:版本号"` - Method string `ddl:"not null;size:6;comment:请求方式"` - Structure map[string]any `ddl:"not null;type:json;comment:请求结构"` - Detail string `ddl:"size:512;comment:描述说明"` - CreatedAt *time.Time `ddl:"NOT NULL;comment:创建时间"` - ExecQueue string `ddl:"size:512;comment:节点执行队列,使用,分割 请求结构确定的,不用每次计算依赖关系"` - Executor map[string]any `ddl:"type:json;comment:节点执行器,格式为Tag:executor;Tag2:executor 未配置为default"` + // tablesync.TableMeta `tableName:"_request" charset:"utf8mb4" comment:"请求参数校验配置"` + Id uint32 `ddl:"primaryKey"` + Debug int8 `ddl:"not null;default:0;comment:是否调试,开发环境可用"` + Tag string `ddl:"not null;size:32;not null;comment:标签名(表别名)"` + Version string `ddl:"not null;size:8;comment:版本号"` + Method string `ddl:"not null;size:6;comment:请求方式"` + Structure map[string]any `ddl:"not null;type:json;comment:请求结构"` + Detail string `ddl:"size:512;comment:描述说明"` + CreatedAt *time.Time `ddl:"NOT NULL;comment:创建时间"` + ExecQueue string `ddl:"size:512;comment:节点执行队列,使用,分割 请求结构确定的,不用每次计算依赖关系"` + Executor map[string]any `ddl:"type:json;comment:节点执行器,格式为Tag:executor;Tag2:executor 未配置为default"` } type Function struct { - tablesync.TableMeta `tableName:"_function" charset:"utf8mb4" comment:"远程函数(暂未使用)"` - Id uint32 `ddl:"primaryKey"` - Debug int8 `ddl:"not null;default:0;comment:是否调试,开发环境可用"` - UserId string `ddl:"not null;comment:管理员id"` - Name string `ddl:"size:50;comment:方法名"` - Arguments string `ddl:"size:100;comment:参数类型列表"` - Demo string `ddl:"size:256;comment:参数示例"` - Type string `ddl:"size:16;comment:返回值类型"` - Tag string `ddl:"not null;size:32;not null;comment:标签名(表别名)"` - Version string `ddl:"not null;size:8;comment:版本号"` - Method string `ddl:"not null;size:5;comment:请求方式"` - Detail string `ddl:"size:512;comment:描述说明"` - CreatedAt *time.Time `ddl:"NOT NULL;comment:创建时间"` - Back string `ddl:"size:128;comment:返回值示例"` + // tablesync.TableMeta `tableName:"_function" charset:"utf8mb4" comment:"远程函数(暂未使用)"` + Id uint32 `ddl:"primaryKey"` + Debug int8 `ddl:"not null;default:0;comment:是否调试,开发环境可用"` + UserId string `ddl:"not null;comment:管理员id"` + Name string `ddl:"size:50;comment:方法名"` + Arguments string `ddl:"size:100;comment:参数类型列表"` + Demo string `ddl:"size:256;comment:参数示例"` + Type string `ddl:"size:16;comment:返回值类型"` + Tag string `ddl:"not null;size:32;not null;comment:标签名(表别名)"` + Version string `ddl:"not null;size:8;comment:版本号"` + Method string `ddl:"not null;size:5;comment:请求方式"` + Detail string `ddl:"size:512;comment:描述说明"` + CreatedAt *time.Time `ddl:"NOT NULL;comment:创建时间"` + Back string `ddl:"size:128;comment:返回值示例"` } diff --git a/config/z_test.go b/config/z_test.go new file mode 100644 index 0000000..e9eca93 --- /dev/null +++ b/config/z_test.go @@ -0,0 +1,88 @@ +package config + +import ( + "context" + "testing" + + "github.com/gogf/gf/v2/frame/g" +) + +type req struct{} + +func (r *req) XxXx() { +} + +type Req struct { + Name string + UserId string +} + +func (r *Req) XxXx() { +} + +type Res struct { + Username string +} + +func TestCall(t *testing.T) { + f := functions{ + funcMap: map[string]*Func{}, + } + + var fn any + + fn = func(ctx context.Context, req *Req) (res any, err error) { + res = &Res{ + Username: req.Name + "_" + req.UserId, + } + + return + } + + f.BindFunc("test", fn) + + ret, err := f.Call(context.TODO(), "test", g.Map{ + "userId": "123", + "name": "123", + "age": 123, + "sex": "123", + "sex2": "123", + "sex3": "", + }) + + ret, err = f.Call(context.TODO(), "test", g.Map{ + "userId": "12311111", + "name": "123", + "age": 123, + "sex": "123", + "sex2": "123", + "sex3": "", + }) + + g.Dump(ret, err) +} + +func BenchmarkName(b *testing.B) { + f := functions{ + funcMap: map[string]*Func{}, + } + + f.BindFunc("test", func(ctx context.Context, req *Req) (res *Res, err error) { + res = &Res{ + Username: req.Name + "_" + req.UserId, + } + + return + }) + + for i := 0; i < b.N; i++ { + f.Call(context.TODO(), "test", g.Map{ + "userId": "123", + "name": "123", + "age": 123, + "sex": "123", + "sex2": "123", + "sex3": "", + }) + } +} diff --git a/demo/READMD.md b/demo/READMD.md new file mode 100644 index 0000000..84c13d5 --- /dev/null +++ b/demo/READMD.md @@ -0,0 +1 @@ +> demo中使用了github.com/glennliao/tablesync 来同步数据库表结构, 不是必须使用 \ No newline at end of file diff --git a/demo/common/db/table.go b/demo/common/db/table.go new file mode 100644 index 0000000..2900b85 --- /dev/null +++ b/demo/common/db/table.go @@ -0,0 +1,63 @@ +package db + +import ( + "context" + "time" + + "github.com/gogf/gf/v2/util/grand" + + "github.com/go-faker/faker/v4" + + "github.com/gogf/gf/v2/frame/g" + + "github.com/gogf/gf/v2/database/gdb" + + "github.com/glennliao/table-sync/tablesync" +) + +type User struct { + Id uint32 `ddl:"primaryKey" faker:"unique"` + Username string `ddl:"size:32;comment:用户名" faker:"username,unique"` + Nickname string `ddl:"size:32;comment:昵称" faker:"name"` + Password string `ddl:"size:128;comment:密码" faker:"password"` + CreatedAt *time.Time + UpdatedAt *time.Time +} + +type Todo struct { + Id uint32 `ddl:"primaryKey"` + UserId uint32 + Content string + CreatedAt *time.Time +} + +func InitTable(ctx context.Context, db gdb.DB) { + syncer := tablesync.Syncer{ + Tables: []tablesync.Table{ + User{}, + Todo{}, + }, + } + err := syncer.Sync(ctx, db) + if err != nil { + g.Log().Fatal(ctx, err) + } + + GenRandomData(ctx, db) +} + +func GenRandomData(ctx context.Context, db gdb.DB) { + for i := 0; i < 10; i++ { + user := User{} + _ = faker.FakeData(&user) + db.Model("user").Insert(user) + + num := grand.N(3, 8) + for i := 0; i < num; i++ { + todo := Todo{} + _ = faker.FakeData(&todo) + todo.UserId = user.Id + db.Model("todo").Insert(todo) + } + } +} diff --git a/demo/custom_access/access.go b/demo/custom_access/access.go new file mode 100644 index 0000000..6ba0ee4 --- /dev/null +++ b/demo/custom_access/access.go @@ -0,0 +1,50 @@ +package main + +import ( + "context" + + "github.com/glennliao/apijson-go/config" +) + +func useAccessFromCode() { + config.RegAccessListProvider("custom", func(ctx context.Context) []config.AccessConfig { + return []config.AccessConfig{ + { + Name: "user", // 数据库表名 + Alias: "User", // 实际访问的名称 + Get: []string{"UNKNOWN"}, + RowKey: "id", // 主键 + FieldsGet: map[string]*config.FieldsGetValue{ + "default": { + In: nil, + // 配置可查看到的字段, key 为数据库内字段名, value 暂保留为空串 + Out: map[string]string{ + "id": "", + "username": "", + }, + }, + }, + }, + { + Name: "todo", // 数据库表名 + Alias: "Todo", // 实际访问的名称 + Get: []string{"UNKNOWN"}, + RowKey: "id", // 主键 + FieldsGet: map[string]*config.FieldsGetValue{ + "default": { + In: nil, + // 配置可查看到的字段, key 为数据库内字段名, value 暂保留为空串 + Out: map[string]string{ + "content": "", + }, + }, + }, + }, + } + }) + + config.RegRequestListProvider("custom", func(ctx context.Context) []config.RequestConfig { + // 此处还未使用到请求, 故返回空 + return []config.RequestConfig{} + }) +} diff --git a/demo/custom_access/config.yaml b/demo/custom_access/config.yaml new file mode 100644 index 0000000..9ffa5c7 --- /dev/null +++ b/demo/custom_access/config.yaml @@ -0,0 +1,3 @@ +database: + default: + link: "sqlite::@file(./test.sqlite3)" \ No newline at end of file diff --git a/demo/custom_access/main.go b/demo/custom_access/main.go new file mode 100644 index 0000000..4ea7251 --- /dev/null +++ b/demo/custom_access/main.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + + "github.com/glennliao/apijson-go/drivers/goframe" + + "github.com/glennliao/apijson-go" + "github.com/glennliao/apijson-go/demo/common/db" + "github.com/glennliao/apijson-go/model" + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" // need import for mysql + _ "github.com/gogf/gf/contrib/drivers/sqlite/v2" // need import for sqlite + "github.com/gogf/gf/v2/frame/g" +) + +var a *apijson.ApiJson + +func init() { + ctx := context.Background() + db.InitTable(ctx, g.DB()) + + initApiJson(ctx) +} + +func initApiJson(ctx context.Context) { + // 启动goFrame driver + goframe.Enable() + + useAccessFromCode() + + a = apijson.New() + + a.Config().AccessListProvider = "custom" + a.Config().RequestListProvider = "custom" + + err := a.Load() + if err != nil { + g.Log().Fatal(ctx, err) + } +} + +func main() { + ctx := context.Background() + + query := a.NewQuery(ctx, model.Map{ + "User[]": model.Map{}, + }) + + ret, err := query.Result() + g.Dump(ret, err) +} diff --git a/demo/go.mod b/demo/go.mod new file mode 100644 index 0000000..2775ac2 --- /dev/null +++ b/demo/go.mod @@ -0,0 +1,49 @@ +module github.com/glennliao/apijson-go/demo + +replace github.com/glennliao/apijson-go => ../ + +require ( + github.com/glennliao/apijson-go v0.0.0-00010101000000-000000000000 + github.com/glennliao/table-sync v0.3.2 + github.com/gogf/gf/contrib/drivers/mysql/v2 v2.3.3 + github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.3.3 + github.com/gogf/gf/v2 v2.6.4 +) + +require ( + github.com/BurntSushi/toml v1.2.0 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/glebarez/go-sqlite v1.17.3 // indirect + github.com/go-faker/faker/v4 v4.3.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-sql-driver/mysql v1.6.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grokify/html-strip-tags-go v0.0.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/samber/lo v1.39.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/sdk v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.16.8 // indirect + modernc.org/mathutil v1.4.1 // indirect + modernc.org/memory v1.1.1 // indirect + modernc.org/sqlite v1.17.3 // indirect +) + +go 1.18 diff --git a/demo/go.sum b/demo/go.sum new file mode 100644 index 0000000..2f77fbd --- /dev/null +++ b/demo/go.sum @@ -0,0 +1,227 @@ +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/glebarez/go-sqlite v1.17.3 h1:Rji9ROVSTTfjuWD6j5B+8DtkNvPILoUC3xRhkQzGxvk= +github.com/glebarez/go-sqlite v1.17.3/go.mod h1:Hg+PQuhUy98XCxWEJEaWob8x7lhJzhNYF1nZbUiRGIY= +github.com/glennliao/table-sync v0.3.2 h1:N0OptsGToEfMdJO+0EZBgBWiFRa6C780Jlp8Uywjg60= +github.com/glennliao/table-sync v0.3.2/go.mod h1:b0H5cHCqi5F6R/NSLYcgGr+kuD51nI907H0pvn9u0Q8= +github.com/go-faker/faker/v4 v4.3.0 h1:UXOW7kn/Mwd0u6MR30JjUKVzguT20EB/hBOddAAO+DY= +github.com/go-faker/faker/v4 v4.3.0/go.mod h1:F/bBy8GH9NxOxMInug5Gx4WYeG6fHJZ8Ol/dhcpRub4= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gogf/gf/contrib/drivers/mysql/v2 v2.3.3 h1:McqosVS9Bm7SzmsMTwfVT0YX6i/Is2aRn/XfqW/0iSI= +github.com/gogf/gf/contrib/drivers/mysql/v2 v2.3.3/go.mod h1:z+/0qiOwMroAnj5ESuobTv0l5P83rf+XR3r6Fj8WJyk= +github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.3.3 h1:pzQ3vV/WKP7jBhPXYEp0a3Xq9eg21wRe8CHcri9qub8= +github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.3.3 h1:hThUH25Bz1jDBDwcVYTeKTGeplN/bZhrFSWeHm9DVwo= +github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.3.3/go.mod h1:RL2xJ0ju5/an2zZj0SBT2KkrMtZ1QOWlpmfc8ZjwHcc= +github.com/gogf/gf/v2 v2.0.0/go.mod h1:apktt6TleWtCIwpz63vBqUnw8MX8gWKoZyxgDpXFtgM= +github.com/gogf/gf/v2 v2.6.4 h1:w7HXdH9mcTsn/aE13CkaDbRArmAL1KS3FuQqDi6u74Y= +github.com/gogf/gf/v2 v2.6.4/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= +github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM= +go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY= +go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM= +go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o= +modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI= +modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= diff --git a/demo/helloworld/config.yaml b/demo/helloworld/config.yaml new file mode 100644 index 0000000..9ffa5c7 --- /dev/null +++ b/demo/helloworld/config.yaml @@ -0,0 +1,3 @@ +database: + default: + link: "sqlite::@file(./test.sqlite3)" \ No newline at end of file diff --git a/demo/helloworld/main.go b/demo/helloworld/main.go new file mode 100644 index 0000000..3378cbf --- /dev/null +++ b/demo/helloworld/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "context" + + "github.com/glennliao/apijson-go/config" + "github.com/glennliao/apijson-go/drivers/goframe" + + "github.com/glennliao/apijson-go" + "github.com/glennliao/apijson-go/demo/common/db" + "github.com/glennliao/apijson-go/model" + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" // need import for mysql + _ "github.com/gogf/gf/contrib/drivers/sqlite/v2" // need import for sqlite + "github.com/gogf/gf/v2/frame/g" +) + +var a *apijson.ApiJson + +func init() { + ctx := context.Background() + db.InitTable(ctx, g.DB()) + + initApiJson(ctx) +} + +func initApiJson(ctx context.Context) { + // 启动goFrame driver + goframe.Enable() + + // goFrame.Enable 默认启用了从数据库读取access,request, + // 所以此处设置nil, 则在不设置access的情况下,直接访问数据库 + config.RegAccessListProvider("db", nil) + config.RegRequestListProvider("db", nil) + + a = apijson.New() + err := a.Load() + if err != nil { + g.Log().Fatal(ctx, err) + } +} + +func main() { + ctx := context.Background() + + query := a.NewQuery(ctx, model.Map{ + "User[]": model.Map{}, + }) + + query.NoAccessVerify = true + + ret, err := query.Result() + g.Dump(ret, err) +} diff --git a/drivers/goframe/all.go b/drivers/goframe/all.go index 4018af1..9415801 100644 --- a/drivers/goframe/all.go +++ b/drivers/goframe/all.go @@ -12,8 +12,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -func init() { - +func Enable() { config.RegAccessListProvider(gfConfig.ProviderName, gfConfig.AccessListDBProvider) config.RegRequestListProvider(gfConfig.ProviderName, gfConfig.RequestListProvider) @@ -34,5 +33,4 @@ func init() { }) } }) - } diff --git a/drivers/goframe/executor/action.go b/drivers/goframe/executor/action.go index d40e799..e65b8aa 100755 --- a/drivers/goframe/executor/action.go +++ b/drivers/goframe/executor/action.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/glennliao/apijson-go/action" + "github.com/glennliao/apijson-go/config" "github.com/glennliao/apijson-go/consts" "github.com/glennliao/apijson-go/model" "github.com/glennliao/apijson-go/util" @@ -17,18 +18,13 @@ type ActionExecutor struct { DbResolver DbResolver } -func (a *ActionExecutor) Do(ctx context.Context, req action.ActionExecutorReq) (ret model.Map, err error) { +func (a *ActionExecutor) Do(ctx context.Context, req action.ExecutorReq) (ret model.Map, err error) { switch req.Method { case http.MethodPost: return a.Insert(ctx, req.Table, req.Data) case http.MethodPut: - for i, _ := range req.Data { - ret, err = a.Update(ctx, req.Table, req.Data[i], req.Where[i]) - if err != nil { - break - } - } + ret, err = a.Update(ctx, req.Table, req.Data, req.Where, req.AccessCondition) if err != nil { ret = model.Map{ "code": 200, @@ -38,12 +34,7 @@ func (a *ActionExecutor) Do(ctx context.Context, req action.ActionExecutorReq) ( case http.MethodDelete: - for i, _ := range req.Data { - ret, err = a.Delete(ctx, req.Table, req.Where[i]) - if err != nil { - break - } - } + ret, err = a.Delete(ctx, req.Table, req.Where, req.AccessCondition) if err != nil { ret = model.Map{ "code": 200, @@ -54,7 +45,7 @@ func (a *ActionExecutor) Do(ctx context.Context, req action.ActionExecutorReq) ( return nil, consts.NewMethodNotSupportErr(req.Method) } -func (a *ActionExecutor) Insert(ctx context.Context, table string, data []model.Map) (ret model.Map, err error) { +func (a *ActionExecutor) Insert(ctx context.Context, table string, data model.Map) (ret model.Map, err error) { result, err := a.DbResolver(ctx).Insert(ctx, table, data) if err != nil { return nil, err @@ -75,10 +66,18 @@ func (a *ActionExecutor) Insert(ctx context.Context, table string, data []model. return ret, nil } -func (a *ActionExecutor) Update(ctx context.Context, table string, data model.Map, where model.Map) (ret model.Map, err error) { +func (a *ActionExecutor) Update(ctx context.Context, table string, data model.Map, where model.Map, accessCondition *config.ConditionRet) (ret model.Map, err error) { + conditions, builders := accessCondition.AllCondition() + //if len(conditions)+len(builders) == 0 { + // return nil, consts.NewValidReqErr("where的值不能为空") + //} + m := a.DbResolver(ctx).Model(table).Ctx(ctx) + whereBuilder := m.Builder() + for k, v := range where { + // TODO 统一到condition中 if strings.HasSuffix(k, consts.OpIn) { if vStr, ok := v.(string); ok { if vStr == "" { @@ -88,18 +87,32 @@ func (a *ActionExecutor) Update(ctx context.Context, table string, data model.Ma m = m.WhereIn(k[0:len(k)-2], v) delete(where, k) continue - } - if k == consts.Raw { + } else if k == consts.Raw { m = m.Where(v.(map[string][]any)) delete(where, k) continue + } else { + whereBuilder = whereBuilder.Where(k, v) } + } - if v == nil || gconv.String(v) == "" { // 暂只处理字符串为空的情况 - return nil, consts.NewValidReqErr("where的值不能为空:" + k) + for _, v := range conditions { + + whereBuilder = whereBuilder.Where(v.Column, v.Args) + + if strings.Contains(v.Column, "?") { + // TODO 校验必须有条件地删除/更新 + //if len(v.Args) == 0 || gconv.String(v.Args[0]) == "" { // 暂只处理字符串为空的情况, // TODO 此处 args[n]? 存在的可能? + // return nil, consts.NewValidReqErr("where的值不能为空:" + v.Column) + //} } } + // 子查询, 看下如何与上面的统一 + for _, v := range builders { + whereBuilder = whereBuilder.Where(v) + } + for k, v := range data { if strings.HasSuffix(k, consts.OpPLus) { field := util.RemoveSuffix(k, consts.OpPLus) @@ -127,7 +140,7 @@ func (a *ActionExecutor) Update(ctx context.Context, table string, data model.Ma } - _ret, err := m.Where(where).Update(data) + _ret, err := m.Where(whereBuilder).Update(data) if err != nil { return nil, err } @@ -145,40 +158,58 @@ func (a *ActionExecutor) Update(ctx context.Context, table string, data model.Ma return ret, err } -func (a *ActionExecutor) Delete(ctx context.Context, table string, where model.Map) (ret model.Map, err error) { - if len(where) == 0 { - return nil, consts.NewValidReqErr("where的值不能为空") - } +func (a *ActionExecutor) Delete(ctx context.Context, table string, where model.Map, accessCondition *config.ConditionRet) (ret model.Map, err error) { + // TODO access 校验判断 + //conditions, builders := accessCondition.AllCondition() + //if len(conditions)+len(builders) == 0 { + // return nil, consts.NewValidReqErr("where的值不能为空") + //} m := a.DbResolver(ctx).Model(table).Ctx(ctx) - for k, v := range where { - - if k == consts.Raw { - m = m.Where(v) - continue - } + whereBuilder := m.Builder() + for k, v := range where { if strings.HasSuffix(k, consts.OpIn) { + if vStr, ok := v.(string); ok { + if vStr == "" { + return nil, consts.NewValidReqErr("where的值不能为空") + } + } m = m.WhereIn(k[0:len(k)-2], v) delete(where, k) continue + } else if k == consts.Raw { + m = m.Where(v.(map[string][]any)) + delete(where, k) + continue + } else { + whereBuilder = whereBuilder.Where(k, v) } - - if gconv.String(v) == "" || v == nil { // 暂只处理字符串为空的情况 - return nil, consts.NewValidReqErr("where的值不能为空:" + k) - } - - m = m.Where(k, v) } - _ret, err := m.Delete() + //for _, v := range conditions { + // + // whereBuilder = whereBuilder.Where(v.Column, v.Args) + // + // if strings.Contains(v.Column, "?") { + // //if len(v.Args) == 0 || gconv.String(v.Args[0]) == "" { // 暂只处理字符串为空的情况, // TODO 此处 args[n]? 存在的可能? + // // return nil, consts.NewValidReqErr("where的值不能为空:" + v.Column) + // //} + // } + // + //} + + //for _, v := range builders { + // whereBuilder = whereBuilder.Where(v) + //} + + _ret, err := m.Where(whereBuilder).Delete() if err != nil { return nil, err } count, err := _ret.RowsAffected() - if err != nil { return nil, err } diff --git a/drivers/goframe/executor/query.go b/drivers/goframe/executor/query.go index 6eb770f..d82c000 100755 --- a/drivers/goframe/executor/query.go +++ b/drivers/goframe/executor/query.go @@ -9,7 +9,6 @@ import ( "github.com/glennliao/apijson-go/consts" "github.com/glennliao/apijson-go/model" "github.com/glennliao/apijson-go/query" - "github.com/glennliao/apijson-go/util" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" @@ -24,7 +23,10 @@ type SqlExecutor struct { Role string // 保存where条件 [ ["user_id",">", 123], ["user_id","<=",345] ] + // dep // TODO 废弃 Where [][]any + whereBuilder []*config.ConditionRet + whereCondition []config.WhereItem accessCondition map[string][]any Columns []string @@ -39,8 +41,7 @@ type SqlExecutor struct { DbResolver DbResolver } -func New(ctx context.Context, config *config.ExecutorConfig) (query.QueryExecutor, error) { - +func New(ctx context.Context, config *config.ExecutorConfig) (query.Executor, error) { return &SqlExecutor{ ctx: ctx, Where: [][]any{}, @@ -57,28 +58,39 @@ func New(ctx context.Context, config *config.ExecutorConfig) (query.QueryExecuto // ParseCondition 解析查询条件 // accessVerify 内部调用时, 不校验是否可使用该种查询方式 -func (e *SqlExecutor) ParseCondition(conditions model.MapStrAny, accessVerify bool) error { - - for key, condition := range conditions { - switch { - case strings.HasSuffix(key, consts.OpIn): - e.parseMultiCondition(util.RemoveSuffix(key, consts.OpIn), condition) - - case strings.HasSuffix(key, consts.OpLike): - e.Where = append(e.Where, []any{key[0 : len(key)-1], consts.SqlLike, gconv.String(condition)}) +func (e *SqlExecutor) ParseCondition(where *config.ConditionRet, accessVerify bool) error { + conditions, builders := where.AllCondition() + if where.IsEmptyResult() { + e.WithEmptyResult = true + return nil + } - case strings.HasSuffix(key, consts.OpRegexp): - e.Where = append(e.Where, []any{key[0 : len(key)-1], consts.SqlRegexp, gconv.String(condition)}) + // for _, condition := range conditions { + // switch { + // case strings.HasSuffix(condition.Column, consts.OpIn): + // e.parseMultiCondition(util.RemoveSuffix(key, consts.OpIn), condition) + // + // case strings.HasSuffix(key, consts.OpLike): + // e.Where = append(e.Where, []any{key[0 : len(key)-1], consts.SqlLike, gconv.String(condition)}) + // + // case strings.HasSuffix(key, consts.OpRegexp): + // e.Where = append(e.Where, []any{key[0 : len(key)-1], consts.SqlRegexp, gconv.String(condition)}) + // + // case key == consts.Raw && !accessVerify: + // e.accessCondition = condition.(map[string][]any) + // + // default: + // e.Where = append(e.Where, []any{key, consts.SqlEqual, condition}) + // } + // } - case key == consts.Raw && !accessVerify: - e.accessCondition = condition.(map[string][]any) + if !accessVerify { + // TODO 此处为 权限access 中强制的条件, 看下调整放到别处处理 + e.whereCondition = append(e.whereCondition, conditions...) - default: - e.Where = append(e.Where, []any{key, consts.SqlEqual, condition}) + for _, builder := range builders { + e.whereBuilder = append(e.whereBuilder, builder) } - } - - if !accessVerify { return nil } @@ -92,8 +104,8 @@ func (e *SqlExecutor) ParseCondition(conditions model.MapStrAny, accessVerify bo tableName := e.config.TableName() - for _, where := range e.Where { - k := dbStyle(e.ctx, tableName, where[0].(string)) + for _, item := range conditions { + k := dbStyle(e.ctx, tableName, item.Column) if val, exists := inFieldsMap[k]; exists { if len(val) == 0 { @@ -104,36 +116,74 @@ func (e *SqlExecutor) ParseCondition(conditions model.MapStrAny, accessVerify bo continue } - op := where[1].(string) - if op == consts.SqlLike { - condition := where[2].(string) - op = consts.OpLike - if strings.HasPrefix(condition, "%") { - op = "%" + op - } - if strings.HasSuffix(condition, "%") { - op = op + "%" - } - } - - if !lo.Contains(val, op) { - - return consts.NewValidReqErr("不允许使用" + where[0].(string) + "的搜索方式:" + op) - } + // op := where[1].(string) + // if op == consts.SqlLike { + // condition := where[2].(string) + // op = consts.OpLike + // if strings.HasPrefix(condition, "%") { + // op = "%" + op + // } + // if strings.HasSuffix(condition, "%") { + // op = op + "%" + // } + // } + + // if !lo.Contains(val, op) { + // return consts.NewValidReqErr("不允许使用" + where[0].(string) + "的搜索方式:" + op) + // } } else { - return consts.NewValidReqErr("不允许使用" + where[0].(string) + "搜索") + return consts.NewValidReqErr("不允许使用 " + item.Column + " 搜索") } } + e.whereCondition = append(e.whereCondition, conditions...) + + for _, builder := range builders { + e.whereBuilder = append(e.whereBuilder, builder) + } + + // for _, where := range e.Where { + // k := dbStyle(e.ctx, tableName, where[0].(string)) + // if val, exists := inFieldsMap[k]; exists { + // + // if len(val) == 0 { + // continue + // } + // + // if val[0] == "*" { + // continue + // } + // + // op := where[1].(string) + // if op == consts.SqlLike { + // condition := where[2].(string) + // op = consts.OpLike + // if strings.HasPrefix(condition, "%") { + // op = "%" + op + // } + // if strings.HasSuffix(condition, "%") { + // op = op + "%" + // } + // } + // + // if !lo.Contains(val, op) { + // + // return consts.NewValidReqErr("不允许使用" + where[0].(string) + "的搜索方式:" + op) + // } + // + // } else { + // return consts.NewValidReqErr("不允许使用" + where[0].(string) + "搜索") + // } + // } + return nil } // ParseCondition 解析批量查询条件 func (e *SqlExecutor) parseMultiCondition(k string, condition any) { - var conditions [][]string - var value = condition + value := condition if _str, ok := condition.(string); ok { for _, s := range strings.Split(_str, ",") { @@ -166,14 +216,12 @@ func (e *SqlExecutor) parseMultiCondition(k string, condition any) { e.Where = append(e.Where, []any{k, "in", value}) } - } var exp = regexp.MustCompile(`^[\s\w][\w()]+`) // 匹配 field, COUNT(field) // ParseCtrl 解析 @column,@group等控制类 func (e *SqlExecutor) ParseCtrl(ctrl model.Map) error { - fieldStyle := e.config.DbFieldStyle tableName := e.config.TableName() for k, v := range ctrl { @@ -186,6 +234,10 @@ func (e *SqlExecutor) ParseCtrl(ctrl model.Map) error { fieldList[i] = exp.ReplaceAllStringFunc(item, func(field string) string { return fieldStyle(e.ctx, tableName, field) }) // 将请求字段转化为数据库字段风格 + + if strings.Contains(fieldList[i], "`") { + return consts.NewValidReqErr("@column err: " + fieldList[i]) + } } switch k { @@ -249,7 +301,6 @@ func (e *SqlExecutor) build() *gdb.Model { whereBuild = whereBuild.WhereIn(key, conditions) } } else { - switch op { case consts.SqlLike: whereBuild = whereBuild.WhereLike(key, value.(string)) @@ -260,10 +311,43 @@ func (e *SqlExecutor) build() *gdb.Model { case consts.SqlEqual: whereBuild = whereBuild.Where(key, value) } + } + } + for _, item := range e.whereCondition { + column := fieldStyle(e.ctx, tableName, item.Column) + switch item.Op { + case config.OpEq: + whereBuild = whereBuild.Where(column, item.Args) + case config.OpNotEq: + whereBuild = whereBuild.WhereNot(column, item.Args) + case config.OpIn: + whereBuild = whereBuild.WhereIn(column, item.Args) + case config.OpRaw: + whereBuild = whereBuild.Where(column, item.Args) } } + for _, item := range e.whereBuilder { + builder := m.Builder() + list, _ := item.AllCondition() + for _, item := range list { + // TODO 此处与上方重复 + column := fieldStyle(e.ctx, tableName, item.Column) + switch item.Op { + case config.OpEq: + builder = builder.Where(column, item.Args) + case config.OpNotEq: + builder = builder.WhereNot(column, item.Args) + case config.OpIn: + builder = builder.WhereIn(column, item.Args) + case config.OpRaw: + builder = builder.Where(column, item.Args) + } + } + whereBuild = whereBuild.Where(builder) + } + m = m.Where(whereBuild) if e.accessCondition != nil { for k, v := range e.accessCondition { @@ -278,8 +362,7 @@ func (e *SqlExecutor) build() *gdb.Model { return m } -func (e *SqlExecutor) column() []string { - +func (e *SqlExecutor) column(m *gdb.Model) []string { outFields := e.config.GetFieldsGetOutByRole() tableName := e.config.TableName() @@ -292,7 +375,7 @@ func (e *SqlExecutor) column() []string { columns = e.config.TableColumns() } - var fields = make([]string, 0, len(columns)) + fields := make([]string, 0, len(columns)) fieldStyle := e.config.JsonFieldStyle dbStyle := e.config.DbFieldStyle @@ -335,22 +418,19 @@ func (e *SqlExecutor) Count() (total int64, err error) { } return total, nil - } func (e *SqlExecutor) List(page int, count int) (list []model.Map, err error) { - if e.WithEmptyResult { return nil, err } m := e.build() - m = m.Fields(e.column()) + m = m.Fields(e.column(m)) m = m.Page(page, count) all, err := m.All() - if err != nil { return nil, err } @@ -369,9 +449,13 @@ func (e *SqlExecutor) One() (model.Map, error) { m := e.build() - m = m.Fields(e.column()) + m = m.Fields(e.column(m)) one, err := m.One() + if one.IsEmpty() { + return nil, err + } + return one.Map(), err } diff --git a/go.mod b/go.mod index 9d538c4..e26d4b2 100755 --- a/go.mod +++ b/go.mod @@ -1,42 +1,37 @@ module github.com/glennliao/apijson-go require ( - github.com/glennliao/table-sync v0.2.1 - github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.3.2 - github.com/gogf/gf/v2 v2.3.2 - github.com/iancoleman/orderedmap v0.2.0 - github.com/samber/lo v1.33.0 + github.com/gogf/gf/v2 v2.6.4 + github.com/iancoleman/orderedmap v0.3.0 + github.com/samber/lo v1.39.0 ) require ( - github.com/BurntSushi/toml v1.1.0 // indirect - github.com/clbanning/mxj/v2 v2.5.5 // indirect - github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/glebarez/go-sqlite v1.17.3 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/BurntSushi/toml v1.2.0 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grokify/html-strip-tags-go v0.0.1 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-colorable v0.1.9 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect - go.opentelemetry.io/otel v1.7.0 // indirect - go.opentelemetry.io/otel/sdk v1.7.0 // indirect - go.opentelemetry.io/otel/trace v1.7.0 // indirect - golang.org/x/exp v0.0.0-20221110155412-d0897a79cd37 // indirect - golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/sdk v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/libc v1.16.8 // indirect - modernc.org/mathutil v1.4.1 // indirect - modernc.org/memory v1.1.1 // indirect - modernc.org/sqlite v1.17.3 // indirect ) go 1.18 diff --git a/go.sum b/go.sum index abe08ab..a9d9ecf 100755 --- a/go.sum +++ b/go.sum @@ -1,211 +1,75 @@ -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= -github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/glebarez/go-sqlite v1.17.3 h1:Rji9ROVSTTfjuWD6j5B+8DtkNvPILoUC3xRhkQzGxvk= -github.com/glebarez/go-sqlite v1.17.3/go.mod h1:Hg+PQuhUy98XCxWEJEaWob8x7lhJzhNYF1nZbUiRGIY= -github.com/glennliao/table-sync v0.2.1 h1:Z/7j4KJoDzrxIdE2GpaBx+qblYaymqH2+KZOAb5GJ0w= -github.com/glennliao/table-sync v0.2.1/go.mod h1:YgVRcaEqwZMQqjN0fuWqOCAo2vMkfbUE9OSXM6jgiYw= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.3.2 h1:tO9ThU69zo6y3G6KBQPgO0MR79ZfdA5HSYsfl73Marg= -github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.3.2/go.mod h1:RL2xJ0ju5/an2zZj0SBT2KkrMtZ1QOWlpmfc8ZjwHcc= -github.com/gogf/gf/v2 v2.0.0/go.mod h1:apktt6TleWtCIwpz63vBqUnw8MX8gWKoZyxgDpXFtgM= -github.com/gogf/gf/v2 v2.3.2 h1:nlJ0zuDWqFb93/faZmr7V+GADx/lzz5Unz/9x6OJ2u8= -github.com/gogf/gf/v2 v2.3.2/go.mod h1:tsbmtwcAl2chcYoq/fP9W2FZf06aw4i89X34nbSHo9Y= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gogf/gf/v2 v2.6.4 h1:w7HXdH9mcTsn/aE13CkaDbRArmAL1KS3FuQqDi6u74Y= +github.com/gogf/gf/v2 v2.6.4/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA= -github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/samber/lo v1.33.0 h1:2aKucr+rQV6gHpY3bpeZu69uYoQOzVhGT3J22Op6Cjk= -github.com/samber/lo v1.33.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg= -go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM= -go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0= -go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= -go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs= -go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20221110155412-d0897a79cd37 h1:wKMvZzBFHbOCGvF2OmxR5Fqv/jDlkt7slnPz5ejEU8A= -golang.org/x/exp v0.0.0-20221110155412-d0897a79cd37/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c= -golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y= -golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY= +go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o= -modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI= -modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= diff --git a/model/model.go b/model/model.go index f6aab7c..33b414f 100755 --- a/model/model.go +++ b/model/model.go @@ -4,10 +4,12 @@ import ( "github.com/gogf/gf/v2/util/gconv" ) -type Map map[string]any -type MapStrStr map[string]string -type MapStrAny map[string]any -type FuncParam map[string]Var +type ( + Map map[string]interface{} + MapStrStr map[string]string + MapStrAny map[string]any + FuncParam map[string]Var +) func (p *FuncParam) Scan(pointer interface{}, mapping ...map[string]string) error { return gconv.Scan(p, pointer, mapping...) diff --git a/query/executor.go b/query/executor.go index ec728e0..6ed51c5 100755 --- a/query/executor.go +++ b/query/executor.go @@ -9,8 +9,8 @@ import ( "github.com/samber/lo" ) -type QueryExecutor interface { - ParseCondition(conditions model.MapStrAny, accessVerify bool) error +type Executor interface { + ParseCondition(conditions *config.ConditionRet, accessVerify bool) error ParseCtrl(ctrl model.Map) error List(page int, count int) (list []model.Map, err error) Count() (total int64, err error) @@ -18,7 +18,7 @@ type QueryExecutor interface { SetEmptyResult() } -type queryExecutorBuilder func(ctx context.Context, config *config.ExecutorConfig) (QueryExecutor, error) +type queryExecutorBuilder func(ctx context.Context, config *config.ExecutorConfig) (Executor, error) var queryExecutorBuilderMap = map[string]queryExecutorBuilder{} @@ -26,7 +26,7 @@ func RegExecutor(name string, e queryExecutorBuilder) { queryExecutorBuilderMap[name] = e } -func NewExecutor(name string, ctx context.Context, config *config.ExecutorConfig) (QueryExecutor, error) { +func NewExecutor(name string, ctx context.Context, config *config.ExecutorConfig) (Executor, error) { if name == "" { name = "default" } @@ -38,6 +38,6 @@ func NewExecutor(name string, ctx context.Context, config *config.ExecutorConfig return nil, consts.NewSysErr("query executor not found: " + name) } -func QueryExecutorList() []string { +func ExecutorList() []string { return lo.Keys(queryExecutorBuilderMap) } diff --git a/query/node.go b/query/node.go index 4d59cce..8c1bd42 100755 --- a/query/node.go +++ b/query/node.go @@ -16,20 +16,6 @@ import ( "github.com/samber/lo" ) -const ( - NodeTypeStruct = iota // 结构节点 - NodeTypeQuery // 查询节点 - NodeTypeRef // 引用节点 - NodeTypeFunc // functions 节点 -) - -type nodeHandler interface { - parse() - fetch() - result() - nodeType() int -} - type Page struct { Count int Page int @@ -62,7 +48,7 @@ type Node struct { simpleReqVal string // 非对象结构 // 节点数据执行器 - executor QueryExecutor + executor Executor startAt time.Time endAt time.Time @@ -95,13 +81,12 @@ type NodeRef struct { node *Node } -/** +/* +* node 生命周期 new -> buildChild -> parse -> fetch -> result */ - func newNode(query *Query, key string, path string, nodeReq any) *Node { - if query.PrintProcessLog { g.Log().Debugf(query.ctx, "【node】(%s) ", path) } @@ -174,7 +159,6 @@ func parseNodeKey(inK string, path string) (k string, isList bool) { } func (n *Node) buildChild() error { - if n.Type == NodeTypeQuery && !util.HasFirstUpKey(n.req) { // 查询节点嵌套查询节点, 目前不支持 return nil } @@ -243,7 +227,6 @@ func (n *Node) buildChild() error { } func (n *Node) parse() { - if n.queryContext.PrintProcessLog { g.Log().Debugf(n.ctx, "【node】(%s) ", n.Path) } @@ -270,34 +253,6 @@ func (n *Node) parse() { if n.queryContext.PrintProcessLog { g.Log().Debugf(n.ctx, "【node】(%s) ", n.Path) } - -} - -func (n *Node) fetch() { - - defer func() { - n.finish = true - n.endAt = time.Now() - if n.queryContext.PrintProcessLog { - g.Log().Debugf(n.ctx, "【node】(%s) ", n.Path) - } - }() - - if n.queryContext.PrintProcessLog { - g.Log().Debugf(n.ctx, "【node】(%s) hasFinish: 【%v】", n.Path, n.finish) - } - - if n.finish { - g.Log().Error(n.ctx, "再次执行", n.Path) - return - } - - if n.err != nil { - return - } - - n.nodeHandler.fetch() - } func (n *Node) Result() (any, error) { @@ -308,5 +263,4 @@ func (n *Node) Result() (any, error) { n.nodeHandler.result() return n.ret, n.err - } diff --git a/query/node_fetch.go b/query/node_fetch.go new file mode 100644 index 0000000..3a13f30 --- /dev/null +++ b/query/node_fetch.go @@ -0,0 +1,32 @@ +package query + +import ( + "time" + + "github.com/gogf/gf/v2/frame/g" +) + +func (n *Node) fetch() { + defer func() { + n.finish = true + n.endAt = time.Now() + if n.queryContext.PrintProcessLog { + g.Log().Debugf(n.ctx, "【node】(%s) ", n.Path) + } + }() + + if n.queryContext.PrintProcessLog { + g.Log().Debugf(n.ctx, "【node】(%s) hasFinish: 【%v】", n.Path, n.finish) + } + + if n.finish { + g.Log().Error(n.ctx, "再次执行", n.Path) + return + } + + if n.err != nil { + return + } + + n.nodeHandler.fetch() +} diff --git a/query/node_handler.go b/query/node_handler.go new file mode 100644 index 0000000..e9d16fb --- /dev/null +++ b/query/node_handler.go @@ -0,0 +1,15 @@ +package query + +const ( + NodeTypeStruct = iota // 结构节点 + NodeTypeQuery // 查询节点 + NodeTypeRef // 引用节点 + NodeTypeFunc // functions 节点 +) + +type nodeHandler interface { + parse() + fetch() + result() + nodeType() int +} diff --git a/query/node_func.go b/query/node_handler_func.go old mode 100755 new mode 100644 similarity index 98% rename from query/node_func.go rename to query/node_handler_func.go index 26cd84d..7496eef --- a/query/node_func.go +++ b/query/node_handler_func.go @@ -19,11 +19,9 @@ func newFuncNode(n *Node) *funcNode { } func (h *funcNode) parse() { - } func (h *funcNode) fetch() { - } func (h *funcNode) result() { @@ -39,8 +37,6 @@ func (h *funcNode) result() { return } - // todo batch support - param := model.Map{} for i, item := range _func.ParamList { @@ -65,14 +61,12 @@ func (h *funcNode) result() { return } if valNode.ret != nil { - switch valNode.ret.(type) { case model.Map: param[item.Name] = util.String(valNode.ret.(model.Map)[paramName]) case string: param[item.Name] = valNode.ret.(string) } - } else { param[item.Name] = util.String(valNode.simpleReqVal) } diff --git a/query/node_query.go b/query/node_handler_query.go old mode 100755 new mode 100644 similarity index 72% rename from query/node_query.go rename to query/node_handler_query.go index c4f681e..a2af3ee --- a/query/node_query.go +++ b/query/node_handler_query.go @@ -38,16 +38,18 @@ func (q *queryNode) parse() { if n.isList { fieldsGet := n.executorConfig.GetFieldsGetByRole() - if *fieldsGet.MaxCount != 0 { - if n.page.Count > *fieldsGet.MaxCount { + if fieldsGet != nil { + if *fieldsGet.MaxCount != 0 { + if n.page.Count > *fieldsGet.MaxCount { - n.err = consts.NewValidReqErr(" > maxCount: " + n.Path) - return + n.err = consts.NewValidReqErr(" > maxCount: " + n.Path) + return + } } } } - var accessWhereCondition model.MapStrAny + var accessWhereCondition *config.ConditionRet setNodeRole(n, n.Key, n.role) n.executorConfig.SetRole(n.role) @@ -69,7 +71,9 @@ func (q *queryNode) parse() { return } - accessWhereCondition = condition.AllWhere() + accessWhereCondition = condition + } else { + accessWhereCondition = &config.ConditionRet{} } queryExecutor, err := NewExecutor(n.executorConfig.Executor(), n.ctx, n.executorConfig) @@ -83,10 +87,13 @@ func (q *queryNode) parse() { // 查询条件 refKeyMap, conditionMap, ctrlMap := parseQueryNodeReq(n.req, n.isList) - n.executor.ParseCtrl(ctrlMap) + n.err = n.executor.ParseCtrl(ctrlMap) + if n.err != nil { + return + } if v, exists := ctrlMap[consts.Column]; exists { - var exp = regexp.MustCompile(`^[\s\w][\w()]+`) // 匹配 field, COUNT(field) + exp := regexp.MustCompile(`^[\s\w][\w()]+`) // 匹配 field, COUNT(field) fieldStr := strings.ReplaceAll(gconv.String(v), ";", ",") @@ -106,7 +113,6 @@ func (q *queryNode) parse() { } } - fmt.Println(fieldList) } err = n.executor.ParseCondition(conditionMap, true) @@ -195,10 +201,10 @@ func (q *queryNode) fetch() { break } - err = n.executor.ParseCondition(model.MapStrAny{ - refK + consts.OpIn: valList, // @ 与 {}&等的结合 id{}@的处理 - }, false) + condition := &config.ConditionRet{} + condition.In(refK, valList) // @ 与 {}&等的结合 id{}@的处理 + err = n.executor.ParseCondition(condition, false) if err != nil { n.err = err return @@ -215,10 +221,11 @@ func (q *queryNode) fetch() { refVal := item[refNode.column] - var refConditionMap = model.MapStrAny{ - refK: refVal, - } - err = n.executor.ParseCondition(refConditionMap, false) + condition := &config.ConditionRet{} + + condition.In(refK, refVal) + + err = n.executor.ParseCondition(condition, false) if err != nil { n.err = err return @@ -270,69 +277,25 @@ func (q *queryNode) fetch() { if n.isList { - // todo 统一func的调用? - // 组装参数 var paramList []model.Map retList := n.ret.([]model.Map) for _, ret := range retList { - param := model.Map{} - for paramI, paramItem := range _func.ParamList { - paramK := paramKeys[paramI] - if paramItem.Name == consts.FunctionOriReqParam { - param[paramItem.Name] = util.String(ret) - } else { - if strings.HasPrefix(paramK, "'") && strings.HasSuffix(paramK, "'") { - param[paramItem.Name] = paramK[1 : len(paramK)-1] - } else { - param[paramItem.Name] = util.String(ret[paramK]) - } - } - } - paramList = append(paramList, param) + paramList = append(paramList, buildFunctionParam(ret, _func, paramKeys)) } - if _func.Batch { - - param := model.Map{} - - valList, err := queryConfig.CallFunc(n.ctx, functionName, param) + for i, param := range paramList { + val, err := queryConfig.CallFunc(n.ctx, functionName, param) if err != nil { n.err = err return } - list := gconv.Interfaces(valList) - for i := range retList { - retList[i][k] = list[i] - } - - } else { - for i, param := range paramList { - val, err := queryConfig.CallFunc(n.ctx, functionName, param) - if err != nil { - n.err = err - return - } - retList[i][k] = val - } + retList[i][k] = val } } else { - param := model.Map{} - for paramI, paramItem := range _func.ParamList { - paramK := paramKeys[paramI] - if paramItem.Name == consts.FunctionOriReqParam { - param[paramItem.Name] = util.String(n.ret) - } else { - if strings.HasPrefix(paramK, "'") && strings.HasSuffix(paramK, "'") { - param[paramItem.Name] = paramK[1 : len(paramK)-1] - } else { - param[paramItem.Name] = util.String(n.ret.(model.Map)[paramK]) - } - } - } - val, err := queryConfig.CallFunc(n.ctx, functionName, param) + val, err := queryConfig.CallFunc(n.ctx, functionName, buildFunctionParam(n.ret.(model.Map), _func, paramKeys)) if err != nil { n.err = err return @@ -341,7 +304,24 @@ func (q *queryNode) fetch() { } } } +} + +func buildFunctionParam(ret model.Map, _func *config.Func, paramKeys []string) model.Map { + param := model.Map{} + for paramI, paramItem := range _func.ParamList { + paramK := paramKeys[paramI] + if paramItem.Name == consts.FunctionOriReqParam { + param[paramItem.Name] = util.String(ret) + } else { + if strings.HasPrefix(paramK, "'") && strings.HasSuffix(paramK, "'") { + param[paramItem.Name] = paramK[1 : len(paramK)-1] + } else { + param[paramItem.Name] = util.String(ret[paramK]) + } + } + } + return param } func (q *queryNode) result() { diff --git a/query/node_ref.go b/query/node_handler_ref.go old mode 100755 new mode 100644 similarity index 100% rename from query/node_ref.go rename to query/node_handler_ref.go diff --git a/query/node_struct.go b/query/node_handler_struct.go old mode 100755 new mode 100644 similarity index 99% rename from query/node_struct.go rename to query/node_handler_struct.go index ca422c0..588bd69 --- a/query/node_struct.go +++ b/query/node_handler_struct.go @@ -52,7 +52,6 @@ func (h *structNode) parse() { return } } - } func (h *structNode) fetch() { @@ -110,7 +109,6 @@ func (h *structNode) result() { } } - } } diff --git a/query/query.go b/query/query.go index 6cf005a..ebe7f46 100755 --- a/query/query.go +++ b/query/query.go @@ -48,12 +48,9 @@ type Query struct { // jsonFieldStyle 数据库返回的字段 JsonFieldStyle config.FieldStyle - - // Config *config.Config } func New(ctx context.Context, qc *config.QueryConfig, req model.Map) *Query { - q := &Query{ queryConfig: qc, } @@ -65,7 +62,6 @@ func New(ctx context.Context, qc *config.QueryConfig, req model.Map) *Query { } func (q *Query) init(ctx context.Context, req model.Map) { - q.ctx = ctx q.req = req @@ -74,7 +70,6 @@ func (q *Query) init(ctx context.Context, req model.Map) { } func (q *Query) Result() (model.Map, error) { - if q.PrintProcessLog { g.Log().Debugf(q.ctx, "【query】 ============ [begin]") g.Log().Debugf(q.ctx, "【query】 ============ [buildNodeTree]") @@ -84,7 +79,6 @@ func (q *Query) Result() (model.Map, error) { q.rootNode = newNode(q, "", "", q.req) err := q.rootNode.buildChild() - if err != nil { return nil, err } @@ -112,7 +106,6 @@ func (q *Query) Result() (model.Map, error) { } resultMap, err := q.rootNode.Result() - if err != nil { if q.rootNode.err != nil { return nil, q.rootNode.err @@ -138,13 +131,12 @@ func (q *Query) fetch() { var prerequisites [][]string analysisRef(q.rootNode, &prerequisites) fetchQueue, err := util.AnalysisOrder(prerequisites) - if err != nil { q.err = err return } - for k, _ := range q.pathNodes { + for k := range q.pathNodes { if !lo.Contains(fetchQueue, k) { fetchQueue = append(fetchQueue, k) } @@ -163,7 +155,6 @@ func (q *Query) fetch() { // 输出节点信息 func (q *Query) printNode(n *Node, deep int) { - for i := 0; i < deep; i++ { fmt.Print("|") } diff --git a/query/util.go b/query/util.go index b03f870..72c5c27 100755 --- a/query/util.go +++ b/query/util.go @@ -14,10 +14,12 @@ import ( ) // parseQueryNodeReq 解析节点请求内容 -func parseQueryNodeReq(reqMap model.Map, isList bool) (refMap model.MapStrStr, where model.MapStrAny, ctrlMap model.Map) { +func parseQueryNodeReq(reqMap model.Map, isList bool) (refMap model.MapStrStr, condition *config.ConditionRet, ctrlMap model.Map) { refMap = model.MapStrStr{} ctrlMap = model.Map{} - where = model.MapStrAny{} + + condition = &config.ConditionRet{} + for k, v := range reqMap { if strings.HasSuffix(k, consts.FunctionsKeySuffix) { @@ -37,7 +39,7 @@ func parseQueryNodeReq(reqMap model.Map, isList bool) (refMap model.MapStrStr, w } } - where[k] = v + condition.Eq(k, v) } } return @@ -77,7 +79,6 @@ func hasAccess(node *Node) (hasAccess bool, condition *config.ConditionRet, err } func getColList(list []model.Map, col string) []any { - set := gset.New() for _, item := range list { set.Add(gconv.String(item[col])) @@ -94,7 +95,6 @@ func setNeedTotal(node *Node) { // setNodeRole 设置节点的@role, 根据 config.DefaultRoleFunc 获取节点最终的@role func setNodeRole(node *Node, tableName string, parenNodeRole string) { - role, ok := node.req[consts.Role] if node.Type != NodeTypeQuery { @@ -120,7 +120,6 @@ func setNodeRole(node *Node, tableName string, parenNodeRole string) { // analysisRef 分析依赖, 将依赖关系保存到prerequisites中 func analysisRef(p *Node, prerequisites *[][]string) { - // 分析依赖关系, 让无依赖的先执行, 然后在执行后续的 for _, node := range p.children { for _, refNode := range node.refKeyMap { diff --git a/test/go.mod b/test/go.mod new file mode 100644 index 0000000..40711fe --- /dev/null +++ b/test/go.mod @@ -0,0 +1,47 @@ +module github.com/glennliao/apijson-go/test + +replace github.com/glennliao/apijson-go => ../ + +require ( + github.com/glennliao/apijson-go v0.0.0-00010101000000-000000000000 + github.com/glennliao/table-sync v0.3.2 + github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.3.3 + github.com/gogf/gf/v2 v2.6.4 +) + +require ( + github.com/BurntSushi/toml v1.2.0 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/glebarez/go-sqlite v1.17.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grokify/html-strip-tags-go v0.0.1 // indirect + github.com/iancoleman/orderedmap v0.3.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/samber/lo v1.39.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/sdk v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.16.8 // indirect + modernc.org/mathutil v1.4.1 // indirect + modernc.org/memory v1.1.1 // indirect + modernc.org/sqlite v1.17.3 // indirect +) + +go 1.18 diff --git a/test/go.sum b/test/go.sum new file mode 100644 index 0000000..623f42c --- /dev/null +++ b/test/go.sum @@ -0,0 +1,224 @@ +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/glebarez/go-sqlite v1.17.3 h1:Rji9ROVSTTfjuWD6j5B+8DtkNvPILoUC3xRhkQzGxvk= +github.com/glebarez/go-sqlite v1.17.3/go.mod h1:Hg+PQuhUy98XCxWEJEaWob8x7lhJzhNYF1nZbUiRGIY= +github.com/glennliao/table-sync v0.3.2 h1:N0OptsGToEfMdJO+0EZBgBWiFRa6C780Jlp8Uywjg60= +github.com/glennliao/table-sync v0.3.2/go.mod h1:b0H5cHCqi5F6R/NSLYcgGr+kuD51nI907H0pvn9u0Q8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.3.3 h1:pzQ3vV/WKP7jBhPXYEp0a3Xq9eg21wRe8CHcri9qub8= +github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.3.3 h1:hThUH25Bz1jDBDwcVYTeKTGeplN/bZhrFSWeHm9DVwo= +github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.3.3/go.mod h1:RL2xJ0ju5/an2zZj0SBT2KkrMtZ1QOWlpmfc8ZjwHcc= +github.com/gogf/gf/v2 v2.0.0/go.mod h1:apktt6TleWtCIwpz63vBqUnw8MX8gWKoZyxgDpXFtgM= +github.com/gogf/gf/v2 v2.6.4 h1:w7HXdH9mcTsn/aE13CkaDbRArmAL1KS3FuQqDi6u74Y= +github.com/gogf/gf/v2 v2.6.4/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= +github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM= +go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY= +go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM= +go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o= +modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI= +modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= diff --git a/test/z_main_test.go b/test/z_main_test.go index 3279493..765f9f8 100755 --- a/test/z_main_test.go +++ b/test/z_main_test.go @@ -5,9 +5,9 @@ import ( "log" "testing" + "github.com/glennliao/apijson-go/drivers/goframe" + "github.com/glennliao/apijson-go" - "github.com/glennliao/apijson-go/action" - _ "github.com/glennliao/apijson-go/drivers/goframe" "github.com/glennliao/apijson-go/drivers/goframe/web" "github.com/glennliao/apijson-go/model" _ "github.com/gogf/gf/contrib/drivers/sqlite/v2" // need import for sqlite @@ -18,14 +18,18 @@ import ( var a *apijson.ApiJson func init() { - a = apijson.Load(App) + // enable goFrame driver + goframe.Enable() - a.RegActionHook(action.Hook{ - For: []string{"asd"}, - }) + // create apiJson app + a = apijson.New() + + err := a.Use(App).Load() + if err != nil { + g.Log().Fatal(context.TODO(), err) + } } -// notice: import section func TestServer(t *testing.T) { if testing.Short() { t.Skip() @@ -36,7 +40,6 @@ func TestServer(t *testing.T) { } func TestQuery(t *testing.T) { - ctx := gctx.New() q := a.NewQuery(ctx, model.Map{ @@ -69,17 +72,14 @@ func TestQuery(t *testing.T) { q.NoAccessVerify = true result, err := q.Result() - if err != nil { log.Fatalf("%+v", err) } g.Dump(result) - } func TestAlias(t *testing.T) { - ctx := gctx.New() q := a.NewQuery(ctx, model.Map{ @@ -92,7 +92,6 @@ func TestAlias(t *testing.T) { q.NoAccessVerify = false result, err := q.Result() - if err != nil { log.Fatalf("%+v", err) } @@ -128,7 +127,6 @@ func BenchmarkName(b *testing.B) { q.NoAccessVerify = true _, err := q.Result() - if err != nil { log.Fatalf("%+v", err) }